diff --git a/docs/client.md b/docs/client.md index 778e66fb..c15f0dfd 100644 --- a/docs/client.md +++ b/docs/client.md @@ -315,19 +315,18 @@ chattool setup workspace ~/workspace/demo --language en 它会生成一套独立骨架,包括: - `AGENTS.md`:模型主协议 -- `MEMORY.md`:跨 session 记忆 -- `README.md`:workspace 的 general-use 入口 -- `projects/`:所有实际工作的执行容器,单任务和多任务 project 都从这里展开 -- `docs/`:长期文档沉淀 -- `core/`:源码仓库目录 +- - `ARCHIVE.md`:归档摘要日志 +- `.trash/`:workspace 级软删除缓冲区 +- `projects/`:所有实际工作的执行容器 +- `archive/`:归档后的历史 project +- - `core/`:源码仓库目录 +- `scripts/`:workspace 级维护脚本目录 - `skills/`:共享 skills 目录 - `public/`:公开网站和发布目录 -默认先用单任务 project;如果是一组围绕同一目标持续推进的大任务,再升级为多任务 project,并在 project 根保留 `PROJECT.md`、`progress.md`、`review.md`。工作区协议现在也会明确:review 由 loop 在模型准备停下时触发;如果是开发任务,每个阶段都要先测试通过、完善文档,再按 `review.md` 的规则完成校验与收尾。 - 默认模板语言是中文;如果你要英文版协议和 onboarding 文件,可以显式传 `--language en`。 -如果目标目录已经是一个已有 workspace,`setup workspace` 不会直接覆盖原有 `AGENTS.md` / `MEMORY.md`;当前更强调通过根目录 `README.md` 和 `projects/README.md` 提供 general-use 入口与结构约定,再逐步完成迁移。 +如果目标目录已经是一个已有 workspace,`setup workspace` 会优先保留现有 `AGENTS.md` 或历史遗留的 `MEMORY.md`,同时补齐当前结构目录。 如果只想先看计划不落盘: diff --git a/docs/env/index.md b/docs/env/index.md index 1b37cdec..6c74ce00 100644 --- a/docs/env/index.md +++ b/docs/env/index.md @@ -51,22 +51,10 @@ chattool setup docker --sudo -i ```bash chattool setup workspace chattool setup workspace ~/workspace/demo -chattool setup workspace ~/workspace/demo --with-opencode-loop -chattool setup opencode --install-only --plugin chatloop ``` 详细文档:[workspace.md](workspace.md) -### ChatLoop Quickstart - -如果你想从零开始体验一次完整的 `PRD.md -> /chatloop -> fresh-start continuation` 流程,可参考: - -- [chatloop-quickstart.md](chatloop-quickstart.md) - -如果你想看一篇更完整的设计复盘,了解为什么 `chatloop` 要向 `auto-loop` 学 continuation 状态机,以及为什么 `opencode run` 不能代表真实 loop 行为,可参考: - -- [../blog/agent-cli/chatloop-from-auto-loop.md](../blog/agent-cli/chatloop-from-auto-loop.md) - ### Lark CLI 配置 使用 `setup lark-cli` 快速安装官方 `lark-cli`,并复用 ChatTool 现有的 Feishu 配置。 diff --git a/docs/env/workspace.md b/docs/env/workspace.md index 84542638..3da6842b 100644 --- a/docs/env/workspace.md +++ b/docs/env/workspace.md @@ -1,28 +1,8 @@ # Workspace 协作脚手架(setup workspace) -`chattool setup workspace` 用来初始化一个围绕核心项目的“人类-AI 协作工作区”。 +`chattool setup workspace` 用来初始化一个围绕核心项目的人类-AI 协作工作区。 -当前版本采用“基础 workspace + 可选配置项”的结构,但任务组织模型已经从旧的 `reports/` / `playgrounds/` 分桶切换到新的 `projects/` 模型。 - -新建工作区时,命令会直接生成可用的 `README.md`、`AGENTS.md`、`MEMORY.md`。如果目标目录已经像一个 workspace,命令不会贸然覆盖现有 `AGENTS.md` / `MEMORY.md`,而是保留现有协议,同时补齐当前 general-use 的 `README.md` 和结构目录。 - -基础目录固定生成: - -- `projects/` -- `reference/` -- `docs/` -- `core/` -- `skills/` -- `public/` - -其中: - -- `projects/`:所有实际进行中的 project 容器根目录;默认从最小 `PRD.md` project 开始 -- `reference/`:跨多个 project 可复用的长期参考材料 -- `core/`:集中放需要加入 workspace 的源码仓库 -- `skills/`:共享 skills 目录,默认放在 workspace 根目录 -- `public/`:用于部署公开网站和相关发布内容 -- `docs/themes/`:按主题整理的长期维护约定,例如 changelog、project 整理与 workspace 维护规则 +当前模板以 `projects/` 为执行中心,同时显式区分活跃工作、归档历史和 workspace 级脚本。生成结果默认对齐当前 Playground 工作区风格,不再默认生成根 `README.md`、`IDENTITY.md` 或 `MEMORY.md`。 ## 1. 基本用法 @@ -35,156 +15,59 @@ chattool setup workspace ~/workspace/demo --language en 命令形态: ```bash -chattool setup workspace [PROFILE] [WORKSPACE_DIR] [--language zh|en] [--with-opencode-loop] [--force] [--dry-run] [-i|-I] +chattool setup workspace [PROFILE] [WORKSPACE_DIR] [--language zh|en] [--force] [--dry-run] [-i|-I] ``` -- `PROFILE`:可选,当前仅支持 `base` -- `WORKSPACE_DIR`:可选,默认当前目录 -- `--language`:模板语言,默认 `zh`,也可显式传 `en` -- `--with-opencode-loop`:启用 OpenCode loop-aware 模板版本,并把全局 `chatloop` plugin / slash commands 安装到 OpenCode home -- `--force`:覆盖已生成文件 -- `--dry-run`:只打印将创建的目录与文件,不写入磁盘 -- `-i / -I`:强制交互 / 禁止交互 - -补充语义: - -- 如果目标目录已存在 `AGENTS.md` / `MEMORY.md` 等 workspace 标记,优先保留现有协议文件,并补齐当前 general-use 的 `README.md` 与 `projects/` 结构,而不是直接改写现有协议。 - ## 2. 基础结构 ```text workspace/ -├── README.md ├── AGENTS.md -├── MEMORY.md ├── TODO.md +├── ARCHIVE.md +├── .trash/ ├── projects/ -├── docs/ +├── archive/ ├── core/ +├── scripts/ ├── skills/ └── public/ ``` +其中: + +- `AGENTS.md`:模型执行协议 +- `TODO.md`:这个 workspace 近期打算做的事 +- `ARCHIVE.md`:归档项目摘要日志 +- `.trash/`:workspace 级软删除缓冲区 +- `projects/`:所有实际工作的执行容器 +- `archive/`:归档后的历史 project +- `scripts/`:workspace 级维护脚本 + ## 3. `projects/` 模型 -### 默认 project +默认 project 形状: ```text -projects/MM-DD-/ +/ PRD.md progress.md memory.md + .trash/ + reports/ + scripts/ playground/ reference/ ``` -如果后续工作自然长出子部分,可以继续在子目录里放新的 `PRD.md`,而不是预先引入复杂项目级别管理结构。 - -源码仓库访问约定: - -- 真实源码仓库默认保留在 `core/` -- 如果当前 project 需要更短的访问路径,可手动在 project 内创建到 `core/` 的符号链接 -- 该符号链接是按需行为,不作为模板默认自动生成 - -workspace-level 参考约定: - -- project 内的 `reference/` 只放本次任务局部参考、样例和阶段归档 -- 如果某类参考材料已经跨多个 project 可复用,应提升到 workspace 根目录 `reference/` -- 如果某类规则已经明显变成长期维护约定,应提升到 `docs/themes/` - -### 设计原则 - -- workspace 根目录文件用于 general-use 协议与跨 session 上下文 -- 实际执行时,应该进入具体 `project` 目录埋头推进 -- `PRD.md` 是唯一主入口,用于定义任务含义、需求、范围、约束和完成标准 -- `memory.md` / `progress.md` 用于补充当前上下文与进展 -- 只有显式触发 `/chatloop ...` 时,`chatloop` 才会在 idle 时 fresh start,重新读取 `PRD.md`(以及必要的 `memory.md` / `progress.md`) - -## 4. 可选配置项 - -交互模式下可以追加额外模块。 - -### OpenCode loop-aware template - -- 使用 `--with-opencode-loop` 时,会切换到 loop-aware workspace 模板版本 -- 同时会自动执行一次 OpenCode CLI 的纯安装(等价于 `chattool setup opencode --install-only`) -- 同时把 `chatloop` 全局安装到 OpenCode home(默认 `~/.config/opencode/`,也可通过 `OPENCODE_HOME` 改写),包括: - - `plugins/chatloop/` - - `command/chatloop.md` - - `command/chatloop-project.md` - - `command/chatloop-status.md` - - `command/chatloop-help.md` - - `command/chatloop-stop.md` -- 该版本适合先完善 `PRD.md`,再通过显式 `/chatloop ...` 触发 fresh-start continuation 的工作流 -- `chatloop` 可从任意 project 子目录触发,会自动向上寻找最近的 `PRD.md` -- 运行后,状态文件写入当前 project 根目录 `.opencode/chatloop.local.md`,事件记录直接追加到 `.opencode/chatloop.events.log` -- 可通过 `/chatloop-status` 查看当前解析到的 project 根目录、状态文件和事件文件 -- `chatloop` 启动首轮就会强制注入 `PRD.md` 路径与读取要求,而不是简单原样转发用户消息 -- 每轮都要求输出 `## Completed`、`## Next Steps` 和 `STATUS: IN_PROGRESS` / `STATUS: COMPLETE` -- bootstrap 首轮不允许直接完成;只有进入后续 continuation 后,completion gate 才会生效 -- 可通过 `/chatloop-project` 直接查看当前解析到的 project 根目录与 `PRD.md` / state / events 路径 -- 只有同时满足 `STATUS: COMPLETE`、`DONE` 且 `Next Steps` 没有未完成项时,插件才会停止 continuation - -### ChatTool - -- 仓库放到 `core/ChatTool/` -- 从仓库内 `skills/` 同步到 workspace 根目录 `./skills/` -- 交互模式下会直接进入该仓库的 GitHub token 输入;默认值优先从当前仓库对应的 `.git-credential` / `.git-credentials` 读取,并以 mask 形式展示,回车可直接复用 - -### RexBlog - -- 仓库放到 `core/RexBlog/` -- 把 `source/_posts` 链接到 `./public/hexo_blog` -- `public/` 根目录默认附带 `README.md`,说明这里用于部署公开网站 -- 交互模式下同样会直接进入 GitHub token 输入,可复用当前仓库 token;若暂时不填,也可稍后进入目标仓库执行 `chattool gh set-token` - -## 5. 设计原则 - -- `workspace` 是基础模型 -- 额外仓库和发布能力通过“可选配置项”叠加 -- 不再为每个场景分叉新的 workspace 命令 -- 默认完整做完后再统一汇报结果 -- 如果是开发任务,每个阶段要先测试通过、完善文档,再按当前 project 的完成标准收尾 - -## 8. Workspace Maintenance - -当前模板还会生成一个共享 skill: - -- `skills/workspace-maintenance/` - -适用于这些场景: - -- 整理 `projects/` 目录 -- 把可复用材料提升到根目录 `reference/` -- 把长期规范提升到 `docs/themes/` -- 校对 workspace 根文档与当前结构是否一致 - -## 6. dry-run - -```bash -chattool setup workspace ~/workspace/demo --dry-run -I -``` - -适合先确认: - -- 将创建哪些目录 -- 将写哪些文件 -- 将启用哪些可选配置项 - -## 7. General-Use Entry - -当前模型不再依赖 `setup.md` 作为主要入口文件。 - -工作区的一般入口改为: - -- `README.md`:给人和模型的 general-use 说明 -- `AGENTS.md`:模型执行协议 -- `MEMORY.md`:跨 session 高优先级上下文 - -如果是已有 workspace,优先保留现有 `AGENTS.md` / `MEMORY.md`,再通过新的 `README.md` 和 `projects/` 结构逐步迁移。 +## 4. 归档模型 -## 9. Quickstart +- 活跃或近期仍在推进的工作保留在 `projects/` +- 明显不再活跃的项目移动到 `archive/YYYY-MM-DD/` +- 归档摘要写入 workspace 根目录 `ARCHIVE.md` +- 归档过程采用“脚本收集候选 + 模型审查 + 更新 `ARCHIVE.md`”的方式 -如果你想看一遍从创建 `PRD.md`、新建 project、再到显式触发 `/chatloop ...` 的完整示例,可参考: +## 5. 可选模块 -- [chatloop-quickstart.md](chatloop-quickstart.md) +- `ChatTool`:仓库放到 `core/ChatTool/`,并把仓库内 `skills/` 同步到 workspace 根目录 `skills/` +- `RexBlog`:仓库放到 `core/RexBlog/`,并把 `source/_posts` 链接到 `public/hexo_blog` diff --git a/src/chattool/setup/elements.py b/src/chattool/setup/elements.py index 8c806fe4..35665a6f 100644 --- a/src/chattool/setup/elements.py +++ b/src/chattool/setup/elements.py @@ -194,7 +194,6 @@ def workspace_setup( dry_run, with_chattool, chattool_source, - with_opencode_loop, ): setup_workspace( profile_name=profile, @@ -205,7 +204,6 @@ def workspace_setup( dry_run=dry_run, with_chattool=with_chattool, chattool_source=chattool_source, - with_opencode_loop=with_opencode_loop, ) @@ -690,13 +688,6 @@ def workspace_setup( "help": "Git URL or local ChatTool repo path used when --with-chattool is enabled.", }, ), - SetupOptionElement( - param_decls=("--with-opencode-loop/--no-opencode-loop",), - kwargs={ - "default": False, - "help": "Use the OpenCode loop-aware workspace template and install the global chatloop plugin and slash commands for OpenCode.", - }, - ), ), ), ) diff --git a/src/chattool/setup/workspace/cli.py b/src/chattool/setup/workspace/cli.py index 549d14e9..30fb6cbb 100644 --- a/src/chattool/setup/workspace/cli.py +++ b/src/chattool/setup/workspace/cli.py @@ -4,7 +4,6 @@ import click -from chattool.setup.opencode import setup_opencode from chattool.interaction import ( BACK_VALUE, abort_if_force_without_tty, @@ -54,8 +53,6 @@ def _plan_workspace( language: str, enabled_options: list[str], profile, - *, - template_variant: str = "default", ) -> tuple[list[Path], dict[Path, str]]: dir_paths = [workspace_dir / rel for rel in BASE_DIRS] dir_paths.extend(workspace_dir / rel for rel in profile.extra_dirs()) @@ -65,7 +62,6 @@ def _plan_workspace( profile, language, enabled_options, - template_variant=template_variant, existing_workspace=existing_workspace, ) planned_files = {workspace_dir / rel: content for rel, content in file_map.items()} @@ -106,7 +102,6 @@ def setup_workspace( dry_run=False, with_chattool=False, chattool_source=None, - with_opencode_loop=False, ): profile_name, workspace_dir = coerce_profile_and_workspace( profile_name, workspace_dir @@ -117,7 +112,6 @@ def setup_workspace( usage = ( "Usage: chattool setup workspace [PROFILE] [WORKSPACE_DIR] " "[--language zh|en] [--with-chattool] [--chattool-source ] " - "[--with-opencode-loop] " "[--force] [--dry-run] [-i|-I]" ) interactive, can_prompt, force_interactive, _, need_prompt = ( @@ -135,7 +129,6 @@ def setup_workspace( "source": chattool_source or workspace_options.CHATTOOL_REPO_URL, }, "rexblog": {"enabled": False, "source": workspace_options.REXBLOG_REPO_URL}, - "opencode_loop": {"enabled": bool(with_opencode_loop)}, } if need_prompt: @@ -158,9 +151,8 @@ def setup_workspace( enabled_options = [ name for name, item in option_settings.items() if item["enabled"] ] - template_variant = "opencode-loop" if option_settings["opencode_loop"]["enabled"] else "default" dir_paths, file_map = _plan_workspace( - workspace_path, language, enabled_options, profile, template_variant=template_variant + workspace_path, language, enabled_options, profile ) if dry_run: @@ -177,9 +169,6 @@ def setup_workspace( write_text_file(path, content, force=force) applied = [] - if option_settings["opencode_loop"]["enabled"]: - setup_opencode(interactive=False, install_only=True, plugin="chatloop") - applied.append(workspace_options.apply_opencode_loop_option(workspace_path)) if option_settings["chattool"]["enabled"]: applied.append( workspace_options.apply_chattool_option( @@ -220,8 +209,3 @@ def setup_workspace( click.echo(f"RexBlog repo: {item['repo_dir']}") click.echo(f"Repo action: {item['repo_action']}") click.echo(f"Public link: {item['public_link']}") - if item["name"] == "opencode_loop": - click.echo(f"OpenCode home: {item['opencode_home']}") - click.echo(f"ChatLoop plugin: {item['plugin_dir']}") - click.echo(f"ChatLoop commands: {item['commands_dir']}") - click.echo("ChatLoop state and event records are written under each project's .opencode/ directory when /chatloop runs.") diff --git a/src/chattool/setup/workspace/core.py b/src/chattool/setup/workspace/core.py index dac244c4..4c1e8750 100644 --- a/src/chattool/setup/workspace/core.py +++ b/src/chattool/setup/workspace/core.py @@ -36,14 +36,11 @@ class BaseProfile(WorkspaceProfile): BASE_DIRS = [ + ".trash", "projects", - "reference", - "docs", - "docs/memory", - "docs/skills", - "docs/themes", - "docs/tools", + "archive", "core", + "scripts", "skills", "public", ] diff --git a/src/chattool/setup/workspace/options.py b/src/chattool/setup/workspace/options.py index b55c1312..f567aae7 100644 --- a/src/chattool/setup/workspace/options.py +++ b/src/chattool/setup/workspace/options.py @@ -13,10 +13,6 @@ ask_text, create_choice, ) -from chattool.setup.opencode_chatloop import ( - build_chatloop_plugin_entry, - resolve_opencode_home, -) from chattool.utils import mask_secret @@ -86,18 +82,6 @@ def _copy_skill_tree(src: Path, dst: Path) -> list[str]: shutil.copytree(skill_dir, target_dir) copied.append(skill_dir.name) return copied -def apply_opencode_loop_option(workspace_dir: Path) -> dict: - opencode_home = resolve_opencode_home() - plugin_dir = opencode_home / "plugins" / "chatloop" - commands_dir = opencode_home / "command" - return { - "name": "opencode_loop", - "workspace_dir": workspace_dir, - "opencode_home": opencode_home, - "plugin_dir": plugin_dir, - "commands_dir": commands_dir, - "plugin_entry": build_chatloop_plugin_entry(opencode_home), - } def _credential_path_from_source(repo_source: str) -> str | None: @@ -246,7 +230,6 @@ def prompt_optional_modules(language: str) -> dict[str, dict]: "source": REXBLOG_REPO_URL, "github_token": None, }, - "opencode_loop": {"enabled": False}, } enable_extras = ask_confirm( @@ -267,10 +250,6 @@ def prompt_optional_modules(language: str) -> dict[str, dict]: choices=[ create_choice("ChatTool -> core/ChatTool + ./skills", "chattool"), create_choice("RexBlog -> core/RexBlog + public/hexo_blog", "rexblog"), - create_choice( - "OpenCode loop support -> .opencode chatloop plugin + commands", - "opencode_loop", - ), ], default_values=[], instruction="", diff --git a/src/chattool/setup/workspace/render.py b/src/chattool/setup/workspace/render.py index 6d159065..57e09720 100644 --- a/src/chattool/setup/workspace/render.py +++ b/src/chattool/setup/workspace/render.py @@ -6,114 +6,107 @@ from .core import WorkspaceProfile -def _read_template(language: str, relative_path: str, *, template_variant: str = "default") -> str: +def _read_template(language: str, relative_path: str) -> str: return ( resources.files("chattool.setup.workspace") .joinpath("templates") - .joinpath(template_variant) + .joinpath("default") .joinpath(language) .joinpath(relative_path) .read_text(encoding="utf-8") ) -def _render_text_template(language: str, relative_path: str, *, template_variant: str = "default", **variables: str) -> str: - content = _read_template(language, relative_path, template_variant=template_variant) +def _render_text_template(language: str, relative_path: str, **variables: str) -> str: + content = _read_template(language, relative_path) for key, value in variables.items(): content = content.replace(f"{{{{{key}}}}}", value) return content -def render_root_readme(language: str, *, template_variant: str = "default") -> str: - return _read_template(language, "README.md", template_variant=template_variant) +def render_projects_readme(language: str) -> str: + return _read_template(language, "projects/README.md") -def render_projects_readme(language: str, *, template_variant: str = "default") -> str: - return _read_template(language, "projects/README.md", template_variant=template_variant) - - -def render_docs_readme(language: str) -> str: - if language == "en": - return "# Docs\n\nDurable notes outside source repositories.\n" - return "# Docs\n\n源码仓库之外的长期文档区。\n" - - -def render_docs_memory_readme(language: str) -> str: - if language == "en": - return "# Memory\n\nDurable status snapshots and long-lived context.\n" - return "# Memory\n\n状态快照与长期上下文。\n" - - -def render_docs_tools_readme(language: str) -> str: - if language == "en": - return "# Tools\n\nTool-specific notes and references.\n" - return "# Tools\n\n工具说明与参考。\n" - - -def render_docs_skills_readme(language: str) -> str: +def render_archive_readme(language: str) -> str: if language == "en": - return "# Skills\n\nReusable skills, patterns, and practice notes.\n" - return "# Skills\n\n可复用技巧、skills 与实践记录。\n" + return ( + "# Archive README\n\n" + "`archive/` stores inactive projects by archive date.\n\n" + "## Structure\n\n" + "```text\narchive/\n README.md\n YYYY-MM-DD/\n /\n```\n\n" + "Move inactive projects into `archive/YYYY-MM-DD/` and summarize them in the workspace root `ARCHIVE.md`.\n" + ) + return ( + "# Archive README\n\n" + "`archive/` 是 Playground 的历史日志区,按归档日期保存已不活跃的 project。\n\n" + "## 目录结构\n\n" + "```text\narchive/\n README.md\n YYYY-MM-DD/\n /\n```\n\n" + "每次归档时,把 project 移到 `archive/YYYY-MM-DD/`,并在 workspace 根目录 `ARCHIVE.md` 中补归档摘要。\n" + ) -def render_reference_readme(language: str) -> str: +def render_archive_md(language: str) -> str: if language == "en": - return "# Reference\n\nReusable workspace-level references shared across multiple projects.\n" - return "# Reference\n\n跨多个 project 可复用的 workspace 级长期参考。\n" + return "# Archive\n\nRecord archived project summaries in reverse chronological order.\n" + return ( + "# Archive\n\n" + "按日期倒序记录归档项目摘要。每个项目都说明“这是做什么的”和“本轮做了什么”。更具体的维护规则见 `archive/README.md`。\n" + ) -def render_docs_themes_readme(language: str) -> str: +def render_todo_md(language: str) -> str: if language == "en": - return "# Themes\n\nLong-lived workspace conventions organized by theme.\n" - return "# Themes\n\n按主题整理的 workspace 长期规范与维护约定。\n" + return "# TODO\n\nNear-term plans for this workspace.\n" + return "# TODO\n\n这里记录这个 workspace 近期打算做的事。\n" -def render_docs_theme_changelog(language: str) -> str: +def render_scripts_readme(language: str) -> str: if language == "en": return ( - "# Changelog Theme\n\n" - "Current workspace changelog rules:\n\n" - "- Maintain `CHANGELOG.md` entries with explicit dates\n" - "- Do not introduce a parallel `release.log` mechanism unless a repo already has one\n" - "- Use project `progress.md` for task process notes, not repo changelogs\n" + "# Scripts\n\n" + "Workspace-level maintenance scripts live here. Keep task-specific scripts inside the relevant project.\n" ) return ( - "# Changelog Theme\n\n" - "当前 workspace 的 changelog 维护约定:\n\n" - "- `CHANGELOG.md` 按日期维护条目\n" - "- 不额外引入 `release.log` 这一类平行机制,除非某个仓库自己已经有明确入口\n" - "- project 的 `progress.md` 用来记录任务推进过程,不替代仓库 changelog\n" + "# Scripts\n\n" + "这里放 workspace 级维护脚本。\n\n" + "约定:\n" + "- 只放跨 project 复用的维护脚本\n" + "- 不要在 workspace 顶层散放脚本或临时文件\n" + "- 业务任务相关脚本,优先放在对应 project 内部\n" ) -def render_workspace_maintenance_skill(language: str) -> tuple[str, str]: +def render_workspace_maintenance_skill() -> tuple[str, str]: skill_md = ( "---\n" "name: workspace-maintenance\n" - "description: Maintain the outer workspace structure. Use when organizing projects, promoting reusable materials into workspace-level reference, or normalizing long-lived rules into docs/themes.\n" - "version: 0.1.0\n" + "description: Maintain the outer workspace structure. Use for project cleanup, archive review, root protocol alignment, and moving files into the proper workspace-level locations.\n" + "version: 0.2.2\n" "---\n\n" "# Workspace Maintenance\n\n" - "Use this skill for outer-workspace cleanup and normalization.\n\n" - "- promote reusable materials into `reference/`\n" - "- normalize long-lived rules into `docs/themes/`\n" - "- keep project-local `reference/` directories task-specific\n" - "- update root docs when workspace structure changes\n" + "Use this skill when maintaining the outer workspace rather than editing a source repository.\n\n" + "- keep active work under `projects/` and archive inactive work into `archive/YYYY-MM-DD/`\n" + "- update `ARCHIVE.md` when projects are archived or restored\n" + "- keep workspace-level scripts under `scripts/`\n" + "- prefer moving files into the nearest `.trash/` instead of deleting them directly\n" + "- keep root protocol files (`AGENTS.md`, `ARCHIVE.md`, `TODO.md`) aligned with the real workspace structure\n" ) skill_zh = ( "---\n" "name: workspace-maintenance\n" - "description: 维护 workspace 外层协作结构。适用于整理 projects、把可复用材料提升到 workspace 级 reference、把长期规范收口到 docs/themes。\n" - "version: 0.1.0\n" + "description: 维护 workspace 外层协作结构。适用于整理活跃项目、归档旧项目、对齐根协议文件,以及把文件移动到正确的 workspace 级目录。\n" + "version: 0.2.2\n" "---\n\n" "# Workspace Maintenance(中文)\n\n" - "用于 workspace 外层整理与规范化。\n\n" - "- 把可复用材料沉淀到 `reference/`\n" - "- 把长期规则收口到 `docs/themes/`\n" - "- 保持 project 内 `reference/` 只服务当前任务\n" - "- workspace 结构变化后同步更新根文档\n" + "用于维护 workspace 外层结构,而不是直接修改源码仓库。\n\n" + "- 活跃工作保留在 `projects/`,不活跃项目归档到 `archive/YYYY-MM-DD/`\n" + "- 发生归档或恢复时同步更新 `ARCHIVE.md`\n" + "- workspace 级维护脚本统一放到 `scripts/`\n" + "- 删除前优先移动到就近的 `.trash/`,不要直接删除\n" + "- 保持根协议文件(`AGENTS.md`、`ARCHIVE.md`、`TODO.md`)与真实结构一致\n" ) - return (skill_md, skill_zh) if language == "en" else (skill_zh, skill_zh) + return skill_md, skill_zh def render_public_readme(language: str) -> str: @@ -122,20 +115,14 @@ def render_public_readme(language: str) -> str: return "# Public\n\n这个目录用于部署公开网站及相关发布产物。\n" -def render_memory_md(language: str, *, template_variant: str = "default") -> str: - return _read_template(language, "MEMORY.md", template_variant=template_variant) - - def render_agents_md( workspace_dir: Path, profile: WorkspaceProfile, language: str, enabled_options: list[str], - *, - template_variant: str = "default", ) -> str: options_text = ", ".join(enabled_options) if enabled_options else "none" - return _render_text_template(language, "AGENTS.md", template_variant=template_variant, ENABLED_OPTIONS=options_text) + return _render_text_template(language, "AGENTS.md", ENABLED_OPTIONS=options_text) def base_file_map( @@ -144,36 +131,25 @@ def base_file_map( language: str, enabled_options: list[str], *, - template_variant: str = "default", existing_workspace: bool, helper_agents_path: str | None = None, - helper_memory_path: str | None = None, + helper_identity_path: str | None = None, ) -> dict[str, str]: file_map = { - "README.md": render_root_readme(language, template_variant=template_variant), - "TODO.md": "# TODO\n\n", - "projects/README.md": render_projects_readme(language, template_variant=template_variant), - "reference/README.md": render_reference_readme(language), - "docs/README.md": render_docs_readme(language), - "docs/memory/README.md": render_docs_memory_readme(language), - "docs/skills/README.md": render_docs_skills_readme(language), - "docs/themes/README.md": render_docs_themes_readme(language), - "docs/themes/changelog.md": render_docs_theme_changelog(language), - "docs/tools/README.md": render_docs_tools_readme(language), + "TODO.md": render_todo_md(language), + "ARCHIVE.md": render_archive_md(language), + "projects/README.md": render_projects_readme(language), + "archive/README.md": render_archive_readme(language), + "scripts/README.md": render_scripts_readme(language), "public/README.md": render_public_readme(language), } - skill_md, skill_zh = render_workspace_maintenance_skill(language) + skill_md, skill_zh = render_workspace_maintenance_skill() file_map["skills/workspace-maintenance/SKILL.md"] = skill_md file_map["skills/workspace-maintenance/SKILL.zh.md"] = skill_zh - agents_content = render_agents_md(workspace_dir, profile, language, enabled_options, template_variant=template_variant) - memory_content = render_memory_md(language, template_variant=template_variant) + agents_content = render_agents_md(workspace_dir, profile, language, enabled_options) if helper_agents_path: file_map[helper_agents_path] = agents_content else: file_map["AGENTS.md"] = agents_content - if helper_memory_path: - file_map[helper_memory_path] = memory_content - else: - file_map["MEMORY.md"] = memory_content file_map.update(profile.extra_files(workspace_dir)) return file_map diff --git a/src/chattool/setup/workspace/templates/default/en/AGENTS.md b/src/chattool/setup/workspace/templates/default/en/AGENTS.md index 1b3ba4fd..eb4eddb6 100644 --- a/src/chattool/setup/workspace/templates/default/en/AGENTS.md +++ b/src/chattool/setup/workspace/templates/default/en/AGENTS.md @@ -4,13 +4,13 @@ ## Core Principles -- Workspace-level files are for project authoring and guidance; actual execution should happen inside the target project directory. -- Use `projects/` as the top-level container for all active work. -- Default to the minimal `PRD.md` workflow instead of precommitting to a complex directory hierarchy. +- Keep only a small set of control files at workspace root; actual execution should happen inside the target project directory. +- Keep all active work under `projects/`; archive inactive projects into `archive/YYYY-MM-DD/`. +- Project structure should stay minimal by default, while naming still allows more flexible grouping. - `PRD.md` records stable requirements, scope, constraints, and completion criteria; progress details belong in `progress.md`. +- `progress.md` is the continuity log for each task. Update it after each substantive action. +- Archiving should not be decided by scripts alone. Scripts can collect candidates and validate rules, but the final archive summary should be reviewed and written into `ARCHIVE.md` by the model. - If requirements are unclear, ask follow-up questions before execution. -- Promote reusable references across projects into `reference/`. -- Promote long-lived maintenance conventions into `docs/themes/`. See `projects/README.md` for concrete project structures and naming rules. @@ -18,52 +18,61 @@ See `projects/README.md` for concrete project structures and naming rules. ```text Workspace/ - README.md - MEMORY.md + AGENTS.md TODO.md + ARCHIVE.md + .trash/ projects/ - reference/ - docs/ + archive/ + YYYY-MM-DD/ core/ + scripts/ skills/ public/ ``` -This outer workspace keeps collaboration artifacts outside the core repositories. +This workspace is an outer collaboration scaffold around source repositories. ## Current Options -- Enabled options: `{{ENABLED_OPTIONS}}` +- Enabled options: `archive/`, `ARCHIVE.md` - Source repositories go under `core/` -- Durable notes live under `docs/` +- Workspace maintenance scripts go under `scripts/` +- The workspace root keeps a `.trash/` directory; prefer moving files there before deleting them directly - Imported shared skills go under `skills/` - Public publish output goes under `public/` -- Cross-project reusable references go under `reference/` -- Theme-organized maintenance rules go under `docs/themes/` +- Archived projects go under `archive/YYYY-MM-DD/` ## Workflow -1. Read `MEMORY.md` before starting work. +1. Read root `AGENTS.md`, then enter the target project. 2. Identify the repo to change under `core/` and the target project under `projects/`. 3. Create or refine `PRD.md` before execution. -4. Keep drafts and local references inside the current project rather than workspace-global buckets. -5. If a project needs a shorter repo path, create an on-demand symlink to `core/` instead of copying the repository. -6. At the end, finish the report and update `MEMORY.md` if durable context changed. -7. If some material is clearly reusable across projects, promote it into workspace-level `reference/` or `docs/themes/` instead of leaving it in one project. +4. Update the current project's `progress.md` after each substantive action. +5. Keep drafts, experiments, and local references inside the current project and place them into the matching subdirectories. +6. Do not write debug temp files into `/tmp`; use the current project's `playground/`. +7. Keep the project root minimal: control files at the root, reports under `reports/`, scripts under `scripts/`. +8. If you use `projects///`, keep `projects//` as an index layer with only `README.md`, `.trash/`, and child project directories. +9. Use `MM-DD-...` for new execution tasks by default; only clearly long-lived stable subprojects should omit the date prefix. +10. Prefer `.trash/` at both workspace and project level; move files there before irreversible deletion. +11. If a project needs a shorter repo path, create an on-demand symlink to `core/` instead of copying the repository. +12. Finish with a report; if archiving happens, update `ARCHIVE.md`. +13. Follow an archive flow of “script candidate collection + model review + `ARCHIVE.md` update”. ## Write Rules | Situation | Write To | |-----------|----------| -| Any active work unit | `projects/MM-DD-/` | -| Cross-project reusable reference | `reference/` | +| Any active work unit | `projects//` or `projects///` | +| Short-term project | Prefer `MM-DD-` | +| Long-lived project | Use a stable name without a date prefix | +| Inactive old project | `archive/YYYY-MM-DD//` | +| Archive summary | `ARCHIVE.md` | | Repositories to change | `core//` | -| Durable context snapshots | `docs/memory/YYYY-MM-DD-status.md` | -| Tool notes | `docs/tools/.md` | -| Reusable skills / practice | `docs/skills/` | -| Theme maintenance rules | `docs/themes/` | +| Workspace maintenance scripts | `scripts/.py` | ## Conventions - Stay within the current task boundary unless the task is explicitly expanded. - State uncertainty explicitly instead of silently assuming. +- Do not scatter standalone scripts or temp files at the workspace root; place durable scripts under `scripts/`. diff --git a/src/chattool/setup/workspace/templates/default/en/MEMORY.md b/src/chattool/setup/workspace/templates/default/en/MEMORY.md deleted file mode 100644 index 5a02d250..00000000 --- a/src/chattool/setup/workspace/templates/default/en/MEMORY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace Memory - -Read this after `AGENTS.md`. Keep high-priority cross-session context here. - -## Current Workspace - -- Projects root: `projects/` -- Core repositories root: `core/` -- Durable status snapshots: `docs/memory/` -- Reusable skills notes: `docs/skills/` -- Tool-specific notes: `docs/tools/` -- Shared imported skills: `skills/` -- Public publish root: `public/` diff --git a/src/chattool/setup/workspace/templates/default/en/README.md b/src/chattool/setup/workspace/templates/default/en/README.md deleted file mode 100644 index 3a6e2a40..00000000 --- a/src/chattool/setup/workspace/templates/default/en/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace - -Human-AI collaboration workspace root. - -This workspace uses `projects/` as the execution container for actual work. Workspace-level files help define protocol and context; project-level files drive execution. - -Conventions: - -- source repositories stay under `core/` by default -- `projects/` only keeps the protocol files, drafts, and references needed for the current work -- if a project needs a shorter repo path, create an on-demand symlink to `core/` inside that project instead of copying the repo -- reusable references across multiple projects should be promoted into the workspace-level `reference/` -- long-lived maintenance conventions should be promoted into `docs/themes/` diff --git a/src/chattool/setup/workspace/templates/default/en/projects/README.md b/src/chattool/setup/workspace/templates/default/en/projects/README.md index a4392136..ee2bb00e 100644 --- a/src/chattool/setup/workspace/templates/default/en/projects/README.md +++ b/src/chattool/setup/workspace/templates/default/en/projects/README.md @@ -11,62 +11,103 @@ Create a new project when the work has its own goal, context, and deliverables. - a bugfix stream - a larger initiative that may later split into multiple tasks -## Naming +## Naming and Grouping -Use `MM-DD-` for each project directory. +Use a date prefix for short-term projects by default: -Start with the simplest structure: +```text +MM-DD- +``` + +The structure may also group work by topic: + +```text +projects/// +``` + +Examples: + +```text +projects/agent-collab/05-25-feishu-links/ +projects/chatrss/05-25-auth-debug/ +``` + +Recommended rules: +- short-term one-off work: prefer `MM-DD-` +- recurring work in one theme: group under `projects///` +- long-lived projects: use a stable name without a date prefix + +## Topic Directory Rules + +When multiple tasks live under the same theme, prefer: ```text -MM-DD-/ +projects/// +``` + +Guidelines: +- `topic/` is an index layer for an ongoing theme or domain, such as `agent-collab/`, `chatrss/`, or `feishu/` +- keep `topic/` minimal; usually only `README.md`, `.trash/`, and child project directories belong there +- `/` is the actual execution unit +- new tasks under a topic should still use a date prefix by default +- only clearly long-lived subprojects should use a stable name + +Avoid: +- grouping when a single flat `projects/MM-DD-/` directory is enough +- deep topic nesting; usually `projects///` is enough +- using the topic directory itself as an execution unit +- placing `reports/`, `playground/`, `reference/`, or task directories without a date prefix directly under `projects//` + +## Default Project Shape + +Start minimal and grow only when the task needs it: + +```text +/ PRD.md progress.md memory.md + .trash/ + reports/ + scripts/ playground/ reference/ ``` -Use this for research, one-off development, focused cleanup, and other work that can stay inside one clear boundary. +Notes: +- `PRD.md`, `progress.md`, and `memory.md` are the control files kept at project root +- `.trash/` is the project-local soft-delete buffer; prefer moving files there before deleting them +- other artifacts should go into the matching subdirectories instead of cluttering project root +- `reports/`, `scripts/`, and similar directories are created on demand -## File roles +## File Roles - `PRD.md`: requirements, scope, constraints, expected deliverables, completion criteria -- `progress.md`: current status, decisions, next step +- `progress.md`: current status, key decisions, next steps; update it after each substantive action - `memory.md`: local context, important files, working notes -- `playground/`: drafts, experiments, temporary outputs -- `reference/`: project-local references and samples +- `reports/`: reports, summaries, staged outputs; use task-specific filenames instead of a generic `report.md` +- `scripts/`: project-local scripts; use purpose-driven names instead of `temp.py` or `run.sh` +- `playground/`: drafts, experiments, intermediate outputs, and debug temp files; do not write these into `/tmp` +- `reference/`: project-local references and examples +- `.trash/`: project-local soft-delete buffer; move files here before permanent deletion Notes: - - `PRD.md` is the primary entry file -- `progress.md` only tracks progress and key decisions +- `progress.md` is the continuity log for the task and should stay current - `memory.md` adds local supporting context +- keep project root minimal instead of scattering reports, scripts, temp output, and miscellaneous files there +- prefer moving files into workspace or project `.trash/` instead of deleting them directly + +## Archiving + +- `projects/` keeps active or recently active work +- move clearly inactive projects into `../archive/YYYY-MM-DD/` +- write archive summaries into workspace root `../ARCHIVE.md` +- archive by moving directories, not by deleting content +- use “script candidate collection + model review” instead of blind script-only archiving ## Repository Access - real source repositories stay under `core/` -- if a project needs a shorter path, create an on-demand symlink inside the project, for example `ln -s ../../core/ChatTool ./ChatTool` +- if a project needs a shorter path, create an on-demand symlink inside the project, for example `ln -s /path/to/ChatTool ./ChatTool` - this symlink is optional and should not be generated by default - -## Workspace-Level Reference - -- the project-local `reference/` directory should only hold task-local references, examples, and stage archives -- if some reference material becomes reusable across multiple projects, promote it into the workspace root `reference/` -- if some reference material is tied to a long-lived source repo, keep a reusable task starter in workspace-level `reference/` and then link `core/` into each concrete project as needed - -## Subdirectory Growth - -If the work later grows sub-parts, do not force a complex management model first. Just create subdirectories and place a new `PRD.md` inside the relevant subdirectory: - -```text -MM-DD-/ - PRD.md - progress.md - memory.md - subtask-a/ - PRD.md - progress.md - memory.md -``` - -Let the hierarchy grow from `PRD.md` itself instead of hardcoding it into the template. diff --git a/src/chattool/setup/workspace/templates/default/zh/AGENTS.md b/src/chattool/setup/workspace/templates/default/zh/AGENTS.md index e9a62270..941cfb9b 100644 --- a/src/chattool/setup/workspace/templates/default/zh/AGENTS.md +++ b/src/chattool/setup/workspace/templates/default/zh/AGENTS.md @@ -4,10 +4,12 @@ ## 核心原则 -- workspace 根目录文件用于辅助创建和约束 project;真正执行任务时,应进入对应 project 目录埋头推进。 -- 所有实际工作统一放到 `projects/` 下。 -- project 默认使用最小 `PRD.md` 工作流,不预先写死复杂目录层级。 +- 外层根目录只保留少量总控文件;真正执行任务时,应进入对应 project 目录埋头推进。 +- 所有实际工作统一放到 `projects/` 下;过时项目归档到 `archive/YYYY-MM-DD/`。 +- project 目录结构默认保持最小化,但命名规则允许更灵活的分组方式。 - `PRD.md` 只记录稳定需求、范围、约束和完成标准;进展细节写入 `progress.md`。 +- `progress.md` 是任务连续性的主日志。每次完成实质动作后,都应及时更新。 +- 归档不应仅靠纯脚本决定。脚本适合做候选收集和规则检查,最终归档摘要应由模型审查候选内容后写入 `ARCHIVE.md`。 - 需求不清晰时,先补问题,再执行。 具体的 project 目录结构与命名规则,统一看 `projects/README.md`。 @@ -16,13 +18,15 @@ ```text Workspace/ - README.md - MEMORY.md + AGENTS.md TODO.md + ARCHIVE.md + .trash/ projects/ - reference/ - docs/ + archive/ + YYYY-MM-DD/ core/ + scripts/ skills/ public/ ``` @@ -31,39 +35,44 @@ Workspace/ ## 当前配置项 -- 已启用项:`{{ENABLED_OPTIONS}}` +- 已启用项:`archive/`、`ARCHIVE.md` - 需要修改的源码仓库放到 `core/` -- 长期文档放到 `docs/` +- 维护脚本统一放到 `scripts/` +- workspace 根目录维护一个 `.trash/`,需要删除或清理文件时,默认优先移动到 `.trash/` - 导入的共享 skills 放到 `skills/` - 对外发布产物放到 `public/` -- 跨多个 project 可复用的长期参考放到 `reference/` -- 按主题整理的长期维护约定放到 `docs/themes/` +- 归档项目放到 `archive/YYYY-MM-DD/` ## 工作流 -1. 开始前先读 `MEMORY.md`。 +1. 先读当前根目录 `AGENTS.md`,再进入目标 project。 2. 识别当前要改的仓库到 `core/`,并进入目标 project。 3. 先补齐 `PRD.md`,再开始执行。 -4. 草稿、实验和局部参考都放在当前 project 内部。 -5. 如需从 project 根目录直接访问源码仓库,可按需手动创建到 `core/` 的符号链接,但不要复制仓库。 -6. 收尾时完成汇报,并在需要时更新 `MEMORY.md`。 -7. 如果某类材料已明显跨多个 project 可复用,优先提升到 workspace 根目录 `reference/` 或 `docs/themes/`,而不是继续堆在单个 project 中。 +4. 每次完成实质动作后,及时更新当前 project 的 `progress.md`。 +5. 草稿、实验和局部参考都放在当前 project 内部,并优先进入匹配职责的子目录。 +6. 项目调试临时文件不要写到 `/tmp`;默认写到当前 project 的 `playground/`。 +7. project 根目录默认只保留 `PRD.md`、`progress.md`、`memory.md` 等控制文件;报告放 `reports/`,脚本放 `scripts/`。 +8. 若使用 `projects///` 主题分组结构,则 `projects//` 根目录只作为索引层,默认只保留 `README.md`、`.trash/` 与子项目目录。 +9. 新建执行任务默认使用 `MM-DD-...` 日期前缀;只有明确的长期稳定子项目才可不带日期前缀。 +10. workspace 和 project 级别都应优先准备 `.trash/`;需要删除文件时,默认先移动到就近的 `.trash/`,而不是直接 `rm`。 +11. 如需从 project 根目录直接访问源码仓库,可按需手动创建到 `core/` 的符号链接,但不要复制仓库。 +12. 收尾时完成汇报;如有归档动作,同步更新 `ARCHIVE.md`。 +13. 归档流程采用“脚本收集候选 + 模型审查 + 更新 `ARCHIVE.md`”的方式。 ## 写入规则 | 情况 | 写入位置 | |-----------|----------| -| 任意实际工作单元 | `projects/MM-DD-/` | -| 跨 project 可复用参考 | `reference/` | +| 任意实际工作单元 | `projects//` 或 `projects///` | +| 短期 project | 推荐 `MM-DD-` | +| 长期 project | 可直接使用稳定名称,不加日期前缀 | +| 已不活跃的旧 project | `archive/YYYY-MM-DD//` | +| 归档摘要 | `ARCHIVE.md` | | 需要修改的源码仓库 | `core//` | -| 状态快照 / 长期上下文 | `docs/memory/YYYY-MM-DD-status.md` | -| 工具使用发现 | `docs/tools/.md` | -| 可复用技巧 / skill 说明 | `docs/skills/` | -| 主题化维护约定 | `docs/themes/` | +| workspace 维护脚本 | `scripts/.py` | ## 约定 - 不要超出当前任务边界;如需扩展,先说明或单独开任务。 - 不确定时要显式说明,不要默默假设。 -- workspace 根目录的 `reference/` 用于沉淀跨多个 project 可复用的长期参考;不要把一次性草稿和局部样例直接堆进去。 -- `docs/themes/` 用于沉淀按主题整理的长期规范,例如 changelog、project 清理、workspace 维护约定。 +- 避免在 workspace 顶层新增零散脚本或临时文件;需要保留的脚本统一放入 `scripts/`。 diff --git a/src/chattool/setup/workspace/templates/default/zh/MEMORY.md b/src/chattool/setup/workspace/templates/default/zh/MEMORY.md deleted file mode 100644 index 45b725e1..00000000 --- a/src/chattool/setup/workspace/templates/default/zh/MEMORY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace Memory - -读完 `AGENTS.md` 后,再读这个文件。这里保留跨 session 的高优先级上下文。 - -## 当前 Workspace - -- 项目根目录:`projects/` -- 核心仓库目录:`core/` -- 持久状态记录目录:`docs/memory/` -- 技能沉淀目录:`docs/skills/` -- 工具经验目录:`docs/tools/` -- 共享 skills 目录:`skills/` -- 对外发布目录:`public/` diff --git a/src/chattool/setup/workspace/templates/default/zh/README.md b/src/chattool/setup/workspace/templates/default/zh/README.md deleted file mode 100644 index 67f7ab91..00000000 --- a/src/chattool/setup/workspace/templates/default/zh/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace - -人类-AI 协作 workspace 根目录。 - -当前 workspace 使用 `projects/` 作为实际工作的执行容器。workspace 根目录文件负责提供协议和上下文,project 内文件负责驱动执行。 - -约定: - -- 需要修改的源码仓库默认保留在 `core/` -- `projects/` 内只保留当前工作需要的协议文件、草稿和参考 -- 如果某个 project 需要更短的源码访问路径,可在 project 内按需手动创建到 `core/` 的符号链接,但不作为默认自动行为 -- 跨多个 project 可复用的长期参考材料,优先沉淀到 workspace 根目录 `reference/` -- 跨多个 project 的整理约定与维护规范,优先沉淀到 `docs/themes/` diff --git a/src/chattool/setup/workspace/templates/default/zh/projects/README.md b/src/chattool/setup/workspace/templates/default/zh/projects/README.md index 0567064e..4f947c1c 100644 --- a/src/chattool/setup/workspace/templates/default/zh/projects/README.md +++ b/src/chattool/setup/workspace/templates/default/zh/projects/README.md @@ -11,64 +11,111 @@ - 一个 bugfix 流 - 一个后续可能拆成多个任务的大目标 -## 命名规则 +## 命名与分组规则 -每个 project 目录默认使用 `MM-DD-`。 +默认推荐短期 project 使用日期前缀: -默认先从最简单的结构开始: +```text +MM-DD- +``` + +但规范允许更灵活的组织方式: + +```text +projects/// +``` + +例如: + +```text +projects/agent-collab/`date`-feishu-links/ +projects/chatrss/`date`-topic-auth-debug/ +``` + +建议: +- **短期、一次性任务**:优先用 `MM-DD-` +- **集中处理同类任务**:可以按 topic 分组,再在 topic 下开子项目 +- **长期 project**:可以去掉日期前缀,直接使用稳定名称 + +## topic 分组目录的推荐写法 + +当你在一段时间内会集中处理同一主题下的多个子任务时,推荐使用 topic 分组: + +```text +projects/// +``` + +推荐原则: +- `topic/` 表示一个持续主题、领域或工作束,例如 `agent-collab/`、`chatrss/`、`feishu/` +- `topic/` 根目录默认只做索引层,保持简洁;通常只保留 `README.md`、`.trash/` 和子项目目录 +- `/` 表示这个主题下的具体任务单元,也是实际执行单元 +- 新建任务默认使用日期前缀,例如:`projects/feishu/05-25-doc-sync/` +- 只有非常明确的长期子项目才使用稳定名称,而且它仍然是独立执行单元,不应把执行产物直接散放在 `topic/` 根目录 + +推荐示例: + +```text +projects/agent-collab/data-feishu-links/ +projects/agent-collab/topic-routing/ +projects/feishu/05-25-doc-sync/ +projects/chatrss/auth-debug/ +``` + +不推荐: +- 为了分组而分组;如果只有一个独立任务,直接放在 `projects/MM-DD-/` 即可 +- topic 层级过深;一般保持 `projects///` 两层就够了 +- 把 topic 目录本身当作执行单元;真正执行仍应落在具体 `/` 目录中 +- 在 `projects//` 根目录直接放 `reports/`、`playground/`、`reference/`、`submission/`、`leaderboard/` 这类执行产物或任务目录,造成主题根目录污染 + +默认先从最简单的结构开始,并按任务需求逐步补目录,而不是一开始把所有可能目录都建满: ```text -MM-DD-/ +/ PRD.md progress.md memory.md + .trash/ + reports/ + scripts/ playground/ reference/ ``` -这适合调研、一次性开发、局部清理等边界清楚的工作。 +其中: +- `PRD.md`、`progress.md`、`memory.md` 是 project 根目录里的控制文件 +- `.trash/` 用于临时收纳待删除文件;默认先移动再清理,避免直接执行 `rm` +- 其他任务产物优先进入对应子目录,不要直接散放在 project 根目录 +- `reports/`、`scripts/` 等目录按任务需要创建;不是每个 project 都必须一次性全部创建 ## 文件职责 - `PRD.md`:需求、范围、约束、预期交付和完成标准 -- `progress.md`:当前进展、关键决定、下一步 +- `progress.md`:当前进展、关键决定、下一步;每次有实质动作后都应更新 - `memory.md`:局部上下文、关键文件、工作记忆 -- `playground/`:草稿、实验、中间产物 +- `reports/`:报告、总结、阶段性输出;文件名应按任务命名,例如 `feishu-session-isolation-report.md`,避免直接使用泛名 `report.md` +- `scripts/`:当前 project 专属脚本;脚本名应体现用途,避免 `temp.py`、`run.sh` 这类过于模糊的命名 +- `playground/`:草稿、实验、中间产物,以及项目调试临时文件;不要把这类文件写到 `/tmp` - `reference/`:当前 project 局部参考和样例 +- `.trash/`:project 内优先级最高的“软删除”缓冲区;需要删除或重构文件时,先移动到这里,确认无误后再统一处理 其中: - `PRD.md` 是主入口 -- `progress.md` 只负责进展和关键决定 +- `progress.md` 是会话连续性的主日志,优先保证最新 - `memory.md` 用于补充局部上下文 +- project 根目录应尽量保持简洁,避免直接堆放报告、脚本、临时输出和杂项文件 +- 删除动作默认优先采用“移动到 workspace 或 project 的 `.trash/`”而不是直接 `rm`;仅在明确确认后才做不可恢复删除 + +## 归档 + +- `projects/` 只保留当前活跃或近期仍在推进的工作 +- 对明显不再活跃的 project,归档到 `../archive/YYYY-MM-DD/` +- 归档摘要写入 workspace 根目录 `../ARCHIVE.md` +- 归档不删除内容,只移动位置,并保留原 project 目录名 +- 归档过程应采用“脚本筛候选 + 模型审查”的方式,而不是纯脚本盲搬 ## 源码仓库访问 - 真实源码仓库默认保留在 `core/` -- 如果当前 project 需要更短的访问路径,可手动在 project 内创建符号链接,例如 `ln -s ../../core/ChatTool ./ChatTool` +- 如果当前 project 需要更短的访问路径,可手动在 project 内创建符号链接,例如 `ln -s /path/to/ChatTool ./ChatTool` - 该符号链接是按需行为,不作为默认模板自动生成 - -## 与 workspace-level `reference/` 的关系 - -- project 内的 `reference/` 只放本次任务局部参考、样例和阶段归档。 -- 如果某类参考材料已经明显跨多个 project 可复用,应提升到 workspace 根目录 `reference/`。 -- 如果某类参考材料长期依托某个源码仓库,可在 workspace-level `reference/` 中保存“任务起手参考”,再在具体 project 下按需链接 `core/`。 - -## 子目录演化 - -如果一个 project 后续自然长出子任务,不需要预先引入固定的项目管理层级。 - -可以直接在当前目录下继续分子目录,并在子目录里放新的 `PRD.md`: - -```text -MM-DD-/ - PRD.md - progress.md - memory.md - subtask-a/ - PRD.md - progress.md - memory.md -``` - -目录层级应该跟着 `PRD.md` 演化,而不是先被模板写死。 diff --git a/src/chattool/setup/workspace/templates/opencode-loop/en/AGENTS.md b/src/chattool/setup/workspace/templates/opencode-loop/en/AGENTS.md deleted file mode 100644 index a29ce0c1..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/en/AGENTS.md +++ /dev/null @@ -1,69 +0,0 @@ -# Workspace Agents - -`AGENTS.md` is the entry guide when a model enters this workspace. - -## Core Principles - -- Workspace-level files are for project authoring and guidance; actual execution should happen inside the target project directory. -- Use `projects/` as the top-level container for all active work. -- This workspace is loop-aware for OpenCode: outer protocol files explain the rules, while inner `chatloop` only runs after an explicit `/chatloop ...` trigger. -- Focus early conversation on refining `PRD.md`; execution should follow `PRD.md` as the primary entry file. -- If a project needs a shorter repo path, create an on-demand symlink to `core/` instead of copying the repository. -- Promote reusable references across projects into `reference/`. -- Promote long-lived maintenance conventions into `docs/themes/`. - -See `projects/README.md` for concrete project structures and naming rules. - -## Architecture - -```text -Workspace/ - README.md - MEMORY.md - TODO.md - projects/ - reference/ - docs/ - core/ - skills/ - public/ -``` - -This outer workspace keeps collaboration artifacts outside the core repositories. - -## Current Options - -- Enabled options: `{{ENABLED_OPTIONS}}` -- Source repositories go under `core/` -- Durable notes live under `docs/` -- Imported shared skills go under `skills/` -- Public publish output goes under `public/` -- Cross-project reusable references go under `reference/` -- Theme-organized maintenance rules go under `docs/themes/` - -## Workflow - -1. Read `MEMORY.md` before starting work. -2. Identify the repo to change under `core/` and the target project under `projects/`. -3. Generate or refine project protocol files before starting execution. -4. Keep drafts and local references inside the current project rather than workspace-global buckets. -5. Only after `/chatloop ...` is triggered should ChatLoop take over idle-time fresh-start continuation from `PRD.md`. -6. At the end, finish the report and update `MEMORY.md` if durable context changed. -7. If some material is clearly reusable across projects, promote it into workspace-level `reference/` or `docs/themes/` instead of leaving it in one project. - -## Write Rules - -| Situation | Write To | -|-----------|----------| -| Any active work unit | `projects/MM-DD-/` | -| Cross-project reusable reference | `reference/` | -| Repositories to change | `core//` | -| Durable context snapshots | `docs/memory/YYYY-MM-DD-status.md` | -| Tool notes | `docs/tools/.md` | -| Reusable skills / practice | `docs/skills/` | -| Theme maintenance rules | `docs/themes/` | - -## Conventions - -- Stay within the current task boundary unless the task is explicitly expanded. -- State uncertainty explicitly instead of silently assuming. diff --git a/src/chattool/setup/workspace/templates/opencode-loop/en/MEMORY.md b/src/chattool/setup/workspace/templates/opencode-loop/en/MEMORY.md deleted file mode 100644 index 5a02d250..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/en/MEMORY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace Memory - -Read this after `AGENTS.md`. Keep high-priority cross-session context here. - -## Current Workspace - -- Projects root: `projects/` -- Core repositories root: `core/` -- Durable status snapshots: `docs/memory/` -- Reusable skills notes: `docs/skills/` -- Tool-specific notes: `docs/tools/` -- Shared imported skills: `skills/` -- Public publish root: `public/` diff --git a/src/chattool/setup/workspace/templates/opencode-loop/en/README.md b/src/chattool/setup/workspace/templates/opencode-loop/en/README.md deleted file mode 100644 index 8a5753ec..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/en/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Workspace - -Human-AI collaboration workspace root. - -This workspace uses `projects/` as the execution container for actual work. Workspace-level files help define protocol and context; project-level files drive execution. - -This template enables OpenCode loop-aware mode: - -- outer protocol files help the model understand document meaning, requirements, and rules -- inner `chatloop` only takes over after an explicit `/chatloop ...` trigger, then handles PRD-driven fresh-start continuation when the model is ready to stop - -Also: - -- source repositories stay under `core/` by default -- if a project needs a shorter repo path, create an on-demand symlink to `core/` inside that project instead of copying the repo -- reusable references across multiple projects should be promoted into the workspace-level `reference/` -- long-lived maintenance conventions should be promoted into `docs/themes/` diff --git a/src/chattool/setup/workspace/templates/opencode-loop/en/projects/README.md b/src/chattool/setup/workspace/templates/opencode-loop/en/projects/README.md deleted file mode 100644 index 0fbb9f4f..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/en/projects/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Projects - -This directory contains all active work. Each project should be self-contained enough that execution can happen mostly inside that project directory. - -This template is designed for OpenCode `chatloop`: - -- early conversation should focus on building `PRD.md` -- the loop plugin only takes over after an explicit `/chatloop ...` trigger, then restarts from `PRD.md` on each idle checkpoint - -## When to create a new project - -Create a new project when the work has its own goal, context, and deliverables. Examples: - -- a research task -- a one-off implementation -- a bugfix stream -- a larger initiative that may later split into multiple tasks - -## Naming - -Use `MM-DD-` for each project directory. - -## Default structure - -Start with the simplest structure: - -```text -MM-DD-/ - PRD.md - progress.md - memory.md - playground/ - reference/ -``` - -Use this for research, one-off development, focused cleanup, and other work that can stay inside one clear work boundary. - -## File roles - -- `PRD.md`: meaning, scope, requirements, constraints, completion target -- `memory.md`: local context, important files, working notes -- `progress.md`: current status, key decisions, next step -- `playground/`: drafts, experiments, temporary outputs -- `reference/`: project-local references and samples - -`PRD.md` is the single primary entry file. `memory.md` and `progress.md` are optional supporting context. - -## Repository Access - -- real source repositories stay under `core/` -- if a project needs a shorter path, create an on-demand symlink inside the project, for example `ln -s ../../core/ChatTool ./ChatTool` -- this symlink is optional and should not be generated by default - -## Workspace-Level Reference - -- the project-local `reference/` directory should only hold task-local references, examples, and stage archives -- if some reference material becomes reusable across multiple projects, promote it into the workspace root `reference/` -- if some reference material is tied to a long-lived source repo, keep a reusable task starter in workspace-level `reference/` and then link `core/` into each concrete project as needed - -## Debugging And Stop Conditions - -- `/chatloop-status` shows the resolved project root, state file, and events file -- ChatLoop state is written to `.opencode/chatloop.local.md` under the resolved project root -- event records are appended to `.opencode/chatloop.events.log` under the resolved project root -- ChatLoop bootstrap already injects the PRD path, project path, and structured progress rules instead of forwarding raw task text alone -- each iteration should emit `## Completed`, `## Next Steps`, and `STATUS: IN_PROGRESS` or `STATUS: COMPLETE` -- the plugin only stops when completion criteria are satisfied, `Next Steps` has no unchecked items, and the model emits both `STATUS: COMPLETE` and `DONE` - -## Subdirectory growth - -If the work later grows sub-parts, do not force a complex project/task management model first. Just create subdirectories and place a new `PRD.md` inside the relevant subdirectory: - -```text -MM-DD-/ - PRD.md - progress.md - memory.md - subtask-a/ - PRD.md - progress.md - memory.md -``` - -This keeps hierarchy growing from `PRD.md` itself instead of forcing an up-front management structure. diff --git a/src/chattool/setup/workspace/templates/opencode-loop/zh/AGENTS.md b/src/chattool/setup/workspace/templates/opencode-loop/zh/AGENTS.md deleted file mode 100644 index 0cc3f6d8..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/zh/AGENTS.md +++ /dev/null @@ -1,70 +0,0 @@ -# Workspace Agents - -`AGENTS.md` 是模型进入这个 workspace 后的入口说明。 - -## 核心原则 - -- workspace 根目录文件用于辅助创建和约束 project;真正执行任务时,应进入对应 project 目录埋头推进。 -- 所有实际工作统一放到 `projects/` 下。 -- 当前 workspace 已启用 OpenCode loop-aware 模式:外层协议负责帮助模型理解规范,内层 `chatloop` 只会在显式触发 `/chatloop ...` 后接管,并围绕 `PRD.md` 做 fresh-start continuation。 -- 前期对话先专注生成和完善 `PRD.md`;`PRD.md` 就绪后,可直接执行,也可在目标目录显式触发 `/chatloop ...`。 -- 如果有辅助上下文,可使用 `memory.md` 和 `progress.md`,但它们不是主入口。 -- 如果当前 project 需要更短的源码路径,可在 project 内按需手动创建到 `core/` 的符号链接,但不作为默认自动行为。 - -具体的 project 目录结构与命名规则,统一看 `projects/README.md`。 - -## 架构 - -```text -Workspace/ - README.md - MEMORY.md - TODO.md - projects/ - reference/ - docs/ - core/ - skills/ - public/ -``` - -这个 workspace 是包裹源码仓库的一层协作脚手架,协作痕迹尽量留在外层。 - -## 当前配置项 - -- 已启用项:`{{ENABLED_OPTIONS}}` -- 需要修改的源码仓库放到 `core/` -- 长期文档放到 `docs/` -- 导入的共享 skills 放到 `skills/` -- 对外发布产物放到 `public/` -- 跨多个 project 可复用的长期参考放到 `reference/` -- 按主题整理的长期维护约定放到 `docs/themes/` - -## 工作流 - -1. 开始前先读 `MEMORY.md`。 -2. 识别当前要改的仓库到 `core/`,并进入目标 project。 -3. 先补齐 `PRD.md`,再决定直接执行,还是在目标目录显式触发 `/chatloop ...`。 -4. 草稿、实验和局部参考都放在当前 project 内部。 -5. 只有显式触发 `/chatloop ...` 时,`chatloop` 才会在模型准备停下时 fresh start,让模型重新阅读 `PRD.md`,必要时再读 `memory.md` / `progress.md`。 -6. 收尾时完成汇报,并在需要时更新 `MEMORY.md`。 -7. 如果某类材料已明显跨多个 project 可复用,优先提升到 workspace 根目录 `reference/` 或 `docs/themes/`,而不是继续堆在单个 project 中。 - -## 写入规则 - -| 情况 | 写入位置 | -|-----------|----------| -| 任意实际工作单元 | `projects/MM-DD-/` | -| 跨 project 可复用参考 | `reference/` | -| 需要修改的源码仓库 | `core//` | -| 状态快照 / 长期上下文 | `docs/memory/YYYY-MM-DD-status.md` | -| 工具使用发现 | `docs/tools/.md` | -| 可复用技巧 / skill 说明 | `docs/skills/` | -| 主题化维护约定 | `docs/themes/` | - -## 约定 - -- 不要超出当前任务边界;如需扩展,先说明或单独开任务。 -- 不确定时要显式说明,不要默默假设。 -- workspace 根目录的 `reference/` 用于沉淀跨多个 project 可复用的长期参考;不要把一次性草稿和局部样例直接堆进去。 -- `docs/themes/` 用于沉淀按主题整理的长期规范,例如 changelog、project 清理、chatloop 使用约定。 diff --git a/src/chattool/setup/workspace/templates/opencode-loop/zh/MEMORY.md b/src/chattool/setup/workspace/templates/opencode-loop/zh/MEMORY.md deleted file mode 100644 index 45b725e1..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/zh/MEMORY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Workspace Memory - -读完 `AGENTS.md` 后,再读这个文件。这里保留跨 session 的高优先级上下文。 - -## 当前 Workspace - -- 项目根目录:`projects/` -- 核心仓库目录:`core/` -- 持久状态记录目录:`docs/memory/` -- 技能沉淀目录:`docs/skills/` -- 工具经验目录:`docs/tools/` -- 共享 skills 目录:`skills/` -- 对外发布目录:`public/` diff --git a/src/chattool/setup/workspace/templates/opencode-loop/zh/README.md b/src/chattool/setup/workspace/templates/opencode-loop/zh/README.md deleted file mode 100644 index 95a283da..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/zh/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Workspace - -人类-AI 协作 workspace 根目录。 - -当前 workspace 使用 `projects/` 作为实际工作的执行容器。workspace 根目录文件负责提供协议和上下文,project 内文件负责驱动执行。 - -当前版本已启用 OpenCode loop-aware 模式: - -- 外层协议负责帮助模型理解文档含义、需求和规范 -- 内层 `chatloop` 只会在显式触发 `/chatloop ...` 后介入,并在模型准备停下时重新进入基于 `PRD.md` 的 fresh-start loop - -另外: - -- 需要修改的源码仓库默认保留在 `core/` -- 如果某个 project 需要更短的源码访问路径,可在 project 内按需手动创建到 `core/` 的符号链接,但不作为默认自动行为 -- 跨多个 project 可复用的长期参考材料,优先沉淀到 workspace 根目录 `reference/` -- 跨多个 project 的整理约定与维护规范,优先沉淀到 `docs/themes/` diff --git a/src/chattool/setup/workspace/templates/opencode-loop/zh/projects/README.md b/src/chattool/setup/workspace/templates/opencode-loop/zh/projects/README.md deleted file mode 100644 index 943b2e09..00000000 --- a/src/chattool/setup/workspace/templates/opencode-loop/zh/projects/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Projects - -这里放当前所有实际进行中的工作。每个 project 都应该足够自洽,使执行阶段可以尽量只在该 project 内部完成。 - -当前版本面向 OpenCode `chatloop`: - -- 前期对话先专注生成和完善 `PRD.md` -- 只有显式触发 `/chatloop ...` 后,loop 插件才会围绕 `PRD.md` 工作;模型每次停下后都会 fresh start,再从头阅读 `PRD.md`(以及必要的 `memory.md` / `progress.md`) - -## 什么时候新开一个 project - -当一项工作有自己明确的目标、上下文和交付物时,就应该新开一个 project。例如: - -- 一次调研 -- 一次性开发任务 -- 一个 bugfix 流 -- 一个后续可能拆成多个任务的大目标 - -## 命名规则 - -每个 project 目录默认使用 `MM-DD-`。 - -## 默认结构 - -默认先从最简单的结构开始: - -```text -MM-DD-/ - PRD.md - memory.md - progress.md - playground/ - reference/ -``` - -这适合调研、一次性开发、局部清理等边界清楚的工作。 - -## 文件职责 - -- `PRD.md`:任务含义、目标、范围、约束、完成标准 -- `memory.md`:局部上下文、关键文件、工作记忆 -- `progress.md`:当前进展、关键决定、下一步 -- `playground/`:草稿、实验、中间产物 -- `reference/`:当前 project 局部参考和样例 - -其中: - -- `PRD.md` 是唯一主入口 -- `memory.md` / `progress.md` 是辅助上下文 -- `chatloop` 每次停下后都会从 `PRD.md` 重新开始理解任务,而不是简单 continue - -## 源码仓库访问 - -- 真实源码仓库默认保留在 `core/` -- 如果当前 project 需要更短的访问路径,可手动在 project 内创建符号链接,例如 `ln -s ../../core/ChatTool ./ChatTool` -- 该符号链接是按需行为,不作为默认模板自动生成 - -## 与 workspace-level `reference/` 的关系 - -- project 内的 `reference/` 只放本次任务局部参考、样例和阶段归档。 -- 如果某类参考材料已经明显跨多个 project 可复用,应提升到 workspace 根目录 `reference/`。 -- 如果某类参考材料长期依托某个源码仓库,可在 workspace-level `reference/` 中保存“任务起手参考”,再在具体 project 下按需链接 `core/`。 - -## 调试与停止 - -- `/chatloop-status` 可查看当前解析到的 project 根目录、状态文件和事件文件 -- `chatloop` 的状态文件写入当前 project 根目录下的 `.opencode/chatloop.local.md` -- 事件记录直接追加到当前 project 根目录下的 `.opencode/chatloop.events.log` -- `chatloop` 首轮就会强制注入 `PRD.md` 路径、project path 和结构化进度规则 -- 每轮应输出 `## Completed`、`## Next Steps` 和 `STATUS: IN_PROGRESS` / `STATUS: COMPLETE` -- 只有当完成标准已满足、`Next Steps` 没有未完成项,并且模型输出 `STATUS: COMPLETE` 与 `DONE` 时,插件才会停止 continuation - -## 子目录拆分 - -如果一个 project 后续自然长出子任务,不需要预先引入复杂的 project/task 双层管理模型。 - -可以直接在当前目录下继续分子目录,并在子目录里放新的 `PRD.md`: - -```text -MM-DD-/ - PRD.md - memory.md - progress.md - subtask-a/ - PRD.md - memory.md - progress.md -``` - -也就是说,层级由 `PRD.md` 自己演化出来,而不是由模板预先强制一套复杂项目级别管理结构。 diff --git a/tests/cli-tests/setup/test_chattool_setup_basic.md b/tests/cli-tests/setup/test_chattool_setup_basic.md index c3489a4c..01eeb518 100644 --- a/tests/cli-tests/setup/test_chattool_setup_basic.md +++ b/tests/cli-tests/setup/test_chattool_setup_basic.md @@ -1,124 +1,5 @@ # test_chattool_setup_basic -测试 `chattool setup` 的基础链路,覆盖 alias/chrome/docker/frp/nodejs/cc-connect/codex/claude/opencode/playground/workspace 等子命令入口。 - -## 元信息 - -- 命令:`chattool setup [args]` -- 目的:验证 setup 工具的基础命令可用。 -- 标签:`cli` -- 前置条件:无 -- 环境准备:按需准备交互参数或依赖。 -- 回滚:删除生成的配置或临时文件。 - -## 用例 1:帮助信息 - -- 初始环境准备: - - 无 -- 相关文件: - - 无 - -预期过程和结果: - 1. 执行 `chattool setup --help`,预期输出子命令列表。 - -参考执行脚本(伪代码): - -```sh -chattool setup --help -``` - -## 用例 2:alias - -- 初始环境准备: - - 准备临时 rc 文件路径(如 `.zshrc`)。 -- 相关文件: - - `/.zshrc` - -预期过程和结果: - 1. 执行 `chattool setup alias --dry-run`,预期输出 alias 变更内容且不写入文件。 - -参考执行脚本(伪代码): - -```sh -chattool setup alias --dry-run -``` - -## 用例 3:chrome/docker/frp/nodejs - -- 初始环境准备: - - 确保可安装依赖。 -- 相关文件: - - 可能生成的安装目录或配置文件。 - -预期过程和结果: - 1. 执行 `chattool setup chrome -i`,预期进入交互式安装流程。 - 2. 执行 `chattool setup docker -i`,预期进入 Docker 环境检查流程;若检测到 Docker / Docker Compose / docker 组缺失,应先提示建议命令,并仅在用户明确确认后执行需要 sudo 的安装步骤。 - 3. 执行 `chattool setup frp -i`,预期进入交互式安装流程。 - 4. 执行 `chattool setup nodejs -i`,预期进入交互式安装流程,并在缺少 `~/.nvm/nvm.sh` 时直接写入内置的 `nvm.sh`。 - -参考执行脚本(伪代码): - -```sh -chattool setup chrome -i -chattool setup docker -i -chattool setup frp -i -chattool setup nodejs -i -``` - -## 用例 4:cc-connect/codex/claude/opencode/lark-cli - -- 初始环境准备: - - 准备 API key 与 base_url。 -- 相关文件: - - 可能生成的配置文件。 - -预期过程和结果: - 1. 执行 `chattool setup cc-connect -i`,预期先检查 Node.js(>= 20),必要时提示执行 `chattool setup nodejs` 安装/升级,然后安装或确认 `cc-connect` CLI。 - 2. 执行 `chattool setup codex -i`,预期在收集配置前先检查 Node.js(>= 20);若不满足,则先提示是否执行 `chattool setup nodejs` 进行安装/升级,然后再进入交互式配置流程。 - 3. 执行 `chattool setup codex -e work` 时,预期可显式从 `OpenAI` profile 读取 `OPENAI_API_KEY`、`OPENAI_API_BASE`、`OPENAI_API_MODEL`。 - 4. 执行 `chattool setup claude -i`,预期在收集配置前先检查 Node.js(>= 20);若不满足,则先提示是否执行 `chattool setup nodejs` 进行安装/升级,然后再进入交互式配置流程。 - 5. 执行 `chattool setup opencode -i`,预期在收集配置前先检查 Node.js(>= 20);若不满足,则先提示是否执行 `chattool setup nodejs` 进行安装/升级,然后再进入交互式配置流程。 - 6. 执行 `chattool setup lark-cli -i`,预期在收集配置前先检查 Node.js(>= 20);若不满足,则先提示是否执行 `chattool setup nodejs` 进行安装/升级;随后可直接复用当前 ChatTool Feishu 配置或显式 `-e` 指定的 profile,并调用官方 `lark-cli config init --app-secret-stdin` 落盘到 `~/.lark-cli/config.json`(或 `LARKSUITE_CLI_CONFIG_DIR/config.json`)。 - -参考执行脚本(伪代码): - -```sh -chattool setup cc-connect -i -chattool setup codex -i -chattool setup codex -e work -chattool setup claude -i -chattool setup opencode -i -chattool setup lark-cli -i -``` - ## 用例 5:workspace -- 初始环境准备: - - 准备一个空目录作为工作区根目录。 -- 相关文件: - - `/README.md` - - `/AGENTS.md` - - `/MEMORY.md` - - `/projects/` - - `/docs/` - - `/core/` - - `/skills/` - - `/public/` - -预期过程和结果: - 1. 执行 `chattool setup workspace [PROFILE] [WORKSPACE_DIR]`,预期可生成围绕核心项目的独立协作骨架,包括 `README.md`、`AGENTS.md`、`MEMORY.md`、`projects/`、`docs/`、`core/`、`skills/`、`public/`,并默认采用 `projects/` 为中心的 project 执行模型。 - 2. 如显式传 `--with-chattool --chattool-source `,预期在 `core/ChatTool/` 下同步仓库,并把 skills 复制到工作区 `skills/`。 - 3. 若目标目录已经像一个 workspace,预期保留现有 `AGENTS.md` / `MEMORY.md`,并通过根目录 `README.md` 与 `projects/README.md` 提供新的 general-use 入口,而不是继续生成 helper files。 - 4. 执行 `chattool setup workspace --dry-run -I`,预期仅打印将创建的目录与文件,不实际写入。 - -参考执行脚本(伪代码): - -```sh -chattool setup workspace /tmp/my-workspace -I -chattool setup workspace /tmp/my-workspace --with-chattool --chattool-source /path/to/ChatTool -I -chattool setup workspace /tmp/my-workspace --dry-run -I -``` - -## 清理 / 回滚 - -- 删除生成的配置文件或安装目录。 +预期生成 `AGENTS.md`、`TODO.md`、`ARCHIVE.md`、`.trash/`、`projects/`、`archive/`、`core/`、`scripts/`、`skills/`、`public/`。 diff --git a/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.md b/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.md index 40abc41c..c06852a6 100644 --- a/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.md +++ b/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.md @@ -1,90 +1,3 @@ # test_chattool_setup_workspace_real_basic -验证 `chattool setup workspace` 在真实 CLI 下的基础落盘、`projects/` 为核心的 workspace 模板、显式英文模板,以及新建工作区和已有工作区两条不同 onboarding 路径。 - -## 元信息 - -- 命令:`chattool setup workspace` -- 目的:验证 workspace 骨架目录、以 `projects/` 为核心的 project 模型、语言选项,以及根目录 `README.md` 的 general-use 入口作用。 -- 标签:`cli` -- 前置条件:无 -- 环境准备:使用临时目录作为 workspace 根目录,不污染仓库。 -- 回滚:测试结束后临时目录自动删除。 - -## 用例 1:base workspace 落盘基础骨架 - -- 初始环境准备: - - 创建空临时目录作为 workspace 根目录。 - -预期过程和结果: - 1. 执行 `chattool setup workspace -I`,预期命令成功。 - 2. 预期生成 `AGENTS.md`、`MEMORY.md`、`README.md`、`projects/README.md`、`docs/README.md`。 - 3. `AGENTS.md` 默认应包含中文的 `projects/` 模型,并说明单任务 project 与多任务 project 两种结构。 - 4. `AGENTS.md` 还应明确:review 由 loop 在模型准备停下时触发;如果是开发任务,每个阶段要先测试通过、文档完善,再按 `review.md` 的规则完成校验与收尾。 - 5. 根目录 `README.md` 应明确:workspace 根目录用于 general-use 协议与上下文,project 目录用于实际执行。 - -参考执行脚本(伪代码): - -```sh -chattool setup workspace /tmp/demo-workspace -I -assert workspace files exist -assert AGENTS.md contains projects model rules -assert README.md contains general-use workspace description -``` - -## 用例 2:显式 `--language en` 时写入英文模板 - -- 初始环境准备: - - 创建空临时目录作为 workspace 根目录。 - -预期过程和结果: - 1. 执行 `chattool setup workspace --language en -I`。 - 2. `AGENTS.md` 应包含 `## Architecture`,且不包含 `## 架构`。 - 3. `README.md` 应使用英文 general-use 文案。 - -参考执行脚本(伪代码): - -```sh -chattool setup workspace /tmp/demo-workspace --language en -I -assert AGENTS.md contains English headings -assert README.md uses English general-use text -``` - -## 用例 3:`--dry-run` 只展示计划,不写文件 - -- 初始环境准备: - - 创建空临时目录作为 workspace 根目录。 - -预期过程和结果: - 1. 执行 `chattool setup workspace --dry-run -I`。 - 2. 预期输出将创建的目录和文件摘要。 - 3. 预期 workspace 根目录不产生任何协议文件与知识目录。 - -参考执行脚本(伪代码): - -```sh -chattool setup workspace /tmp/demo-workspace --dry-run -I -assert output mentions dry run actions -assert workspace remains empty -``` - -## 用例 4:已有 workspace 时保留现有协议,并通过根目录 README 提供当前入口 - -- 初始环境准备: - - 创建一个已有 `AGENTS.md`、`MEMORY.md` 的 workspace 目录。 - -预期过程和结果: - 1. 执行 `chattool setup workspace -I`。 - 2. 原有 `AGENTS.md`、`MEMORY.md` 保持不变。 - 3. 不应生成 `AGENTS.generated.md`、`MEMORY.generated.md` 辅助文件。 - 4. 根目录 `README.md` 应作为当前 workspace 的 general-use 入口存在。 - -参考执行脚本(伪代码): - -```sh -prepare existing AGENTS.md and MEMORY.md -chattool setup workspace /tmp/demo-workspace -I -assert existing protocol files unchanged -assert helper generated files do not exist -assert README.md exists -``` +验证 `chattool setup workspace` 会生成当前外层模板:根目录只保留 `AGENTS.md`、`TODO.md`、`ARCHIVE.md` 这三个控制文件。 diff --git a/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.py b/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.py index a7b40929..b71ce045 100644 --- a/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.py +++ b/tests/cli-tests/setup/test_chattool_setup_workspace_real_basic.py @@ -25,53 +25,41 @@ def test_setup_workspace_base_creates_scaffold(tmp_path: Path): result = _run_chattool_setup_workspace([str(workspace_dir), "-I"]) assert result.returncode == 0, result.stderr - assert (workspace_dir / "README.md").exists() + assert not (workspace_dir / "README.md").exists() assert (workspace_dir / "AGENTS.md").exists() - assert (workspace_dir / "MEMORY.md").exists() + assert not (workspace_dir / "IDENTITY.md").exists() + assert not (workspace_dir / "MEMORY.md").exists() + assert (workspace_dir / "ARCHIVE.md").exists() + assert (workspace_dir / "TODO.md").exists() + assert (workspace_dir / ".trash").exists() assert (workspace_dir / "projects" / "README.md").exists() - assert (workspace_dir / "reference" / "README.md").exists() + assert (workspace_dir / "archive" / "README.md").exists() + assert (workspace_dir / "scripts" / "README.md").exists() + assert not (workspace_dir / "reference" / "README.md").exists() assert (workspace_dir / "core").exists() - assert (workspace_dir / "docs" / "README.md").exists() - assert (workspace_dir / "docs" / "memory" / "README.md").exists() - assert (workspace_dir / "docs" / "skills" / "README.md").exists() - assert (workspace_dir / "docs" / "themes" / "README.md").exists() - assert (workspace_dir / "docs" / "themes" / "changelog.md").exists() - assert (workspace_dir / "docs" / "tools" / "README.md").exists() assert (workspace_dir / "skills" / "workspace-maintenance" / "SKILL.md").exists() assert (workspace_dir / "skills" / "workspace-maintenance" / "SKILL.zh.md").exists() - readme = (workspace_dir / "README.md").read_text(encoding="utf-8") agents = (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") - memory = (workspace_dir / "MEMORY.md").read_text(encoding="utf-8") + todo = (workspace_dir / "TODO.md").read_text(encoding="utf-8") projects = (workspace_dir / "projects" / "README.md").read_text(encoding="utf-8") - reference = (workspace_dir / "reference" / "README.md").read_text(encoding="utf-8") - themes = (workspace_dir / "docs" / "themes" / "README.md").read_text(encoding="utf-8") + archive = (workspace_dir / "archive" / "README.md").read_text(encoding="utf-8") + scripts = (workspace_dir / "scripts" / "README.md").read_text(encoding="utf-8") + skill_zh = (workspace_dir / "skills" / "workspace-maintenance" / "SKILL.zh.md").read_text(encoding="utf-8") assert "## 架构" in agents - assert "## 写入规则" in agents - assert "- 所有实际工作统一放到 `projects/` 下。" in agents - assert "- project 默认使用最小 `PRD.md` 工作流,不预先写死复杂目录层级。" in agents - assert "projects/MM-DD-/" in agents - assert "reference/" in agents - assert "docs/themes/" in agents - assert "projects/MM-DD-/" not in agents - assert "tasks//" not in agents - assert "docs/memory/YYYY-MM-DD-status.md" in agents - assert "core/" in agents - assert "docs/" in agents - assert "`projects/` 作为实际工作的执行容器" in readme - assert "符号链接" in readme - assert "reference/" in readme + assert "AGENTS.md" in agents + assert "TODO.md" in agents + assert "ARCHIVE.md" in agents + assert "Workspace/\n AGENTS.md\n TODO.md\n ARCHIVE.md" in agents + assert "IDENTITY.md" not in agents + assert "这里记录这个 workspace 近期打算做的事" in todo assert "PRD.md" in projects - assert "TASK.md" not in projects - assert "review.md" not in projects - assert "workspace-level `reference/`" in projects - assert "跨多个 project" in reference - assert "按主题整理" in themes - assert "## 当前 Workspace" in memory - assert "- 核心仓库目录:`core/`" in memory - assert "- 项目根目录:`projects/`" in memory - assert "- 持久状态记录目录:`docs/memory/`" in memory - assert "- 技能沉淀目录:`docs/skills/`" in memory + assert "reports/" in projects + assert "topic 分组" in projects + assert "Archive README" in archive + assert "workspace 级维护脚本" in scripts + assert "docs/themes" not in skill_zh + assert "TODO.md" in skill_zh def test_setup_workspace_english_language_creates_english_templates(tmp_path: Path): @@ -82,13 +70,11 @@ def test_setup_workspace_english_language_creates_english_templates(tmp_path: Pa ) assert result.returncode == 0, result.stderr - readme = (workspace_dir / "README.md").read_text(encoding="utf-8") agents = (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") - memory = (workspace_dir / "MEMORY.md").read_text(encoding="utf-8") + todo = (workspace_dir / "TODO.md").read_text(encoding="utf-8") assert "## Architecture" in agents assert "## 架构" not in agents - assert "## Current Workspace" in memory - assert "Human-AI collaboration workspace root." in readme + assert "Near-term plans" in todo def test_setup_workspace_with_chattool_syncs_repo_and_skills(tmp_path: Path): @@ -130,17 +116,10 @@ def test_setup_workspace_existing_workspace_keeps_protocol_files(tmp_path: Path) workspace_dir = tmp_path / "workspace" workspace_dir.mkdir() (workspace_dir / "AGENTS.md").write_text("legacy agents\n", encoding="utf-8") - (workspace_dir / "MEMORY.md").write_text("legacy memory\n", encoding="utf-8") result = _run_chattool_setup_workspace([str(workspace_dir), "-I"]) assert result.returncode == 0, result.stderr - assert (workspace_dir / "AGENTS.md").read_text( - encoding="utf-8" - ) == "legacy agents\n" - assert (workspace_dir / "MEMORY.md").read_text( - encoding="utf-8" - ) == "legacy memory\n" - assert not (workspace_dir / "AGENTS.generated.md").exists() - assert not (workspace_dir / "MEMORY.generated.md").exists() - assert (workspace_dir / "README.md").exists() + assert (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") == "legacy agents\n" + assert not (workspace_dir / "README.md").exists() + assert not (workspace_dir / "IDENTITY.md").exists() diff --git a/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.md b/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.md index 2fbd3f4d..75082469 100644 --- a/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.md +++ b/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.md @@ -1,156 +1,3 @@ # test_chattool_setup_workspace_mock_basic -## Case 1: missing workspace dir should trigger interactive prompt - -### 初始环境准备 - -- 在 mock CLI 环境下调用 `chattool setup workspace`,不提供 `WORKSPACE_DIR`。 -- stub 掉文本输入,避免真实 TTY 依赖。 - -### 预期过程和结果 - -- 命令应进入 workspace 路径补全流程,而不是直接报缺参。 -- 给出路径后,应调用 workspace setup 主逻辑。 - -### 参考执行脚本(伪代码) - -```sh -mock ask_text -> /tmp/workspace -run chattool setup workspace -assert setup callback received workspace path -``` - -## Case 2: default language should be Chinese in dry-run output - -### 初始环境准备 - -- 创建临时目录作为 workspace 根目录。 - -### 预期过程和结果 - -- 执行 `chattool setup workspace --dry-run -I`。 -- 输出应包含中文 dry-run 摘要。 -- 目录内不应出现生成文件。 - -### 参考执行脚本(伪代码) - -```sh -run chattool setup workspace /tmp/workspace --dry-run -I -assert output mentions Chinese dry run summary -assert no generated files exist -``` - -## Case 3: explicit `--language en` should switch templates to English - -### 初始环境准备 - -- 创建临时目录作为 workspace 根目录。 - -### 预期过程和结果 - -- 执行 `chattool setup workspace --language en -I`。 -- 生成的 `AGENTS.md` 应包含英文标题 `## Architecture`。 -- 不应保留中文标题 `## 架构`。 -- 根目录 `README.md` 应使用英文 general-use 文案。 - -### 参考执行脚本(伪代码) - -```sh -run chattool setup workspace /tmp/workspace --language en -I -assert AGENTS.md contains English headings -assert README.md contains English general-use text -``` - -## Case 4: interactive extra repos should enter repo token input directly - -### 初始环境准备 - -- 在 mock CLI 环境下调用 `chattool setup workspace`。 -- 交互中启用 `chattool` 和 `rexblog` 两个额外模块。 -- mock 当前仓库 `.git-credential` 中已存在 GitHub token。 - -### 预期过程和结果 - -- 选择额外模块后,应直接进入 `ChatTool` 和 `RexBlog` 的 token 输入。 -- token 默认值应来自当前仓库对应的 git credential,并以 mask 形式展示在输入标签中。 -- 回车应保留当前 token;空值则表示跳过。 -- 若保留或输入 token,应在 clone 前把它传给对应 repo 的 HTTPS credential 配置流程。 - -### 参考执行脚本(伪代码) - -```sh -mock current repo credential token -> github_pat_xxx -run chattool setup workspace -assert token inputs appear directly for ChatTool and RexBlog -assert token is passed to cubenlp/ChatTool.git and RexWzh/RexBlog -``` - -## Case 5: workspace should use `projects/` container instead of `reports/` and `playgrounds/` - -### 初始环境准备 - -- 创建空 workspace 临时目录。 - -### 预期过程和结果 - -- 执行 `chattool setup workspace -I`。 -- 应生成 `projects/README.md`。 -- 不应再生成 `reports/README.md` 或 `playgrounds/README.md`。 -- `AGENTS.md` 与 `MEMORY.md` 应说明新的 `projects/` 模型,而不是旧的 `reports/` / `playgrounds/` 分桶模型。 - -### 参考执行脚本(伪代码) - -```sh -run chattool setup workspace /tmp/workspace -I -assert projects/README.md exists -assert reports/README.md does not exist -assert playgrounds/README.md does not exist -assert AGENTS.md describes projects model -``` - -## Case 6: existing workspace should keep current protocol files and rely on workspace README guidance - -### 初始环境准备 - -- 创建一个已有 `AGENTS.md` 和 `MEMORY.md` 的 workspace 目录。 - -### 预期过程和结果 - -- 再次执行 `chattool setup workspace -I`。 -- 原有 `AGENTS.md` 和 `MEMORY.md` 不应被覆盖。 -- 不应额外生成 `AGENTS.generated.md` 和 `MEMORY.generated.md`。 -- 根目录 `README.md` 应作为当前 workspace 的 general-use 说明存在。 - -### 参考执行脚本(伪代码) - -```sh -prepare existing AGENTS.md and MEMORY.md -run chattool setup workspace /tmp/workspace -I -assert existing files unchanged -assert helper generated files do not exist -assert README.md exists -``` - -## Case 7: enabling OpenCode loop support should switch template variant and install global chatloop assets - -### 初始环境准备 - -- 创建空 workspace 临时目录。 - -### 预期过程和结果 - -- 执行 `chattool setup workspace -I --with-opencode-loop`。 -- 应生成 loop-aware 的 `README.md`、`AGENTS.md`、`projects/README.md`。 -- 应在 OpenCode home 下写入 `plugins/chatloop/` 完整插件目录。 -- 应在 `plugins/chatloop/` 下执行一次依赖安装。 -- 应在 OpenCode home 下写入 `command/chatloop.md`、`chatloop-help.md`、`chatloop-stop.md`。 - -### 参考执行脚本(伪代码) - -```sh -run chattool setup workspace /tmp/workspace -I --with-opencode-loop -assert ~/.config/opencode/plugins/chatloop/index.ts exists -assert ~/.config/opencode/plugins/chatloop/package.json exists -assert ~/.config/opencode/command/chatloop.md exists -assert AGENTS.md contains loop-aware wording -``` +验证 workspace 模板不再生成根 `README.md`、`IDENTITY.md` 或 `MEMORY.md`,而是以 `AGENTS.md`、`TODO.md`、`ARCHIVE.md` 为外层控制文件。 diff --git a/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.py b/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.py index 1d0c34df..03c0a81f 100644 --- a/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.py +++ b/tests/mock-cli-tests/setup/test_chattool_setup_workspace_mock_basic.py @@ -3,7 +3,7 @@ import pytest from chattool.client.main import cli -from chattool.setup.workspace.render import render_agents_md, render_memory_md, render_projects_readme, render_root_readme +from chattool.setup.workspace.render import render_agents_md, render_projects_readme pytestmark = pytest.mark.mock_cli @@ -40,26 +40,22 @@ def fake_text(label, default=None, **kwargs): assert selected["workspace"] is not None assert (tmp_path / "workspace" / "core").exists() assert (tmp_path / "workspace" / "projects").exists() - assert (tmp_path / "workspace" / "reference").exists() + assert (tmp_path / "workspace" / "archive").exists() + assert (tmp_path / "workspace" / "scripts").exists() assert (tmp_path / "workspace" / "skills").exists() assert (tmp_path / "workspace" / "public").exists() - assert (tmp_path / "workspace" / "README.md").exists() + assert (tmp_path / "workspace" / ".trash").exists() + assert not (tmp_path / "workspace" / "README.md").exists() assert "Workspace 初始化完成。" in result.output -def test_workspace_template_variants_can_be_loaded(): - assert "Workspace" in render_root_readme("en", template_variant="default") - assert "Workspace" in render_root_readme("en", template_variant="opencode-loop") - assert "Projects" in render_projects_readme("en", template_variant="default") - assert "Projects" in render_projects_readme("en", template_variant="opencode-loop") - assert "项目根目录" in render_memory_md("zh", template_variant="default") - assert "项目根目录" in render_memory_md("zh", template_variant="opencode-loop") +def test_workspace_templates_can_be_loaded(): + assert "Projects" in render_projects_readme("en") assert "已启用项" in render_agents_md( Path("/tmp/demo"), profile=type("P", (), {"extra_files": lambda self, workspace_dir: {}})(), language="zh", - enabled_options=["opencode_loop"], - template_variant="opencode-loop", + enabled_options=[], ) @@ -125,81 +121,6 @@ def test_setup_workspace_interactive_can_enable_chattool(tmp_path, monkeypatch, assert "Enabled options: chattool, rexblog" in result.output assert any("ChatTool github_token" in item for item in prompts) assert any("RexBlog github_token" in item for item in prompts) - assert any(("当前" in item) or ("current" in item) for item in prompts) - - -def test_setup_workspace_interactive_configures_repo_tokens( - tmp_path, monkeypatch, runner -): - values = iter([str(tmp_path / "workspace")]) - configured = [] - - monkeypatch.setattr( - "chattool.setup.workspace.cli.ask_text", - lambda label, default=None, **kwargs: next(values), - ) - monkeypatch.setattr( - "chattool.setup.workspace.options.ask_confirm", - lambda message, default=False: True, - ) - monkeypatch.setattr( - "chattool.setup.workspace.options.ask_checkbox_with_controls", - lambda *args, **kwargs: ["chattool", "rexblog"], - ) - monkeypatch.setattr( - "chattool.setup.workspace.options._read_current_repo_github_token", - lambda: "github_pat_existing_token", - ) - monkeypatch.setattr( - "chattool.setup.workspace.options.ask_text", - lambda label, default="", password=False, style=None: default, - ) - monkeypatch.setattr( - "chattool.setup.workspace.options.apply_chattool_option", - lambda workspace_dir, source, interactive, can_prompt, github_token=None: ( - configured.append((source, github_token)), - { - "name": "chattool", - "repo_dir": workspace_dir / "core" / "ChatTool", - "repo_action": "cloned", - "copied_skills": ["demo-skill"], - }, - )[1], - ) - monkeypatch.setattr( - "chattool.setup.workspace.options.apply_rexblog_option", - lambda workspace_dir, source, interactive, can_prompt, github_token=None: ( - configured.append((source, github_token)), - { - "name": "rexblog", - "repo_dir": workspace_dir / "core" / "RexBlog", - "repo_action": "cloned", - "public_link": workspace_dir / "public" / "hexo_blog", - }, - )[1], - ) - monkeypatch.setattr( - "chattool.setup.workspace.cli.resolve_interactive_mode", - lambda interactive, auto_prompt_condition: ( - interactive, - True, - False, - True, - True, - ), - ) - - result = runner.invoke(cli, ["setup", "workspace"]) - - assert result.exit_code == 0 - assert ( - "https://github.com/cubenlp/ChatTool.git", - "github_pat_existing_token", - ) in configured - assert ( - "https://github.com/RexWzh/RexBlog", - "github_pat_existing_token", - ) in configured def test_setup_workspace_dry_run_writes_nothing(tmp_path, runner): @@ -225,105 +146,40 @@ def test_setup_workspace_explicit_english_language(tmp_path, runner): assert result.exit_code == 0 agents = (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") - readme = (workspace_dir / "README.md").read_text(encoding="utf-8") + todo = (workspace_dir / "TODO.md").read_text(encoding="utf-8") assert "Current Options" in agents assert "已启用项" not in agents - assert "Human-AI collaboration workspace root." in readme + assert "Near-term plans" in todo -def test_setup_workspace_uses_projects_model(tmp_path, runner): +def test_setup_workspace_uses_current_workspace_model(tmp_path, runner): workspace_dir = tmp_path / "workspace" result = runner.invoke(cli, ["setup", "workspace", str(workspace_dir), "-I"]) assert result.exit_code == 0 assert (workspace_dir / "projects" / "README.md").exists() - assert (workspace_dir / "reference" / "README.md").exists() - assert not (workspace_dir / "reports" / "README.md").exists() - assert not (workspace_dir / "playgrounds" / "README.md").exists() + assert (workspace_dir / "archive" / "README.md").exists() + assert (workspace_dir / "scripts" / "README.md").exists() + assert not (workspace_dir / "README.md").exists() agents = (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") - memory = (workspace_dir / "MEMORY.md").read_text(encoding="utf-8") - reference = (workspace_dir / "reference" / "README.md").read_text(encoding="utf-8") + archive = (workspace_dir / "archive" / "README.md").read_text(encoding="utf-8") + todo = (workspace_dir / "TODO.md").read_text(encoding="utf-8") assert "projects/" in agents - assert "reference/" in agents - assert "reports/" not in agents - assert "projects/" in memory - assert "reference/" not in memory - assert "跨多个 project" in reference + assert "archive/" in agents + assert "scripts/" in agents + assert "Workspace/\n AGENTS.md\n TODO.md\n ARCHIVE.md" in agents + assert "归档日期" in archive + assert "近期打算做的事" in todo def test_setup_workspace_existing_workspace_keeps_protocol_files(tmp_path, runner): workspace_dir = tmp_path / "workspace" workspace_dir.mkdir() (workspace_dir / "AGENTS.md").write_text("legacy agents\n", encoding="utf-8") - (workspace_dir / "MEMORY.md").write_text("legacy memory\n", encoding="utf-8") result = runner.invoke(cli, ["setup", "workspace", str(workspace_dir), "-I"]) assert result.exit_code == 0 - assert not (workspace_dir / "AGENTS.generated.md").exists() - assert not (workspace_dir / "MEMORY.generated.md").exists() - assert (workspace_dir / "README.md").exists() - assert (workspace_dir / "AGENTS.md").read_text( - encoding="utf-8" - ) == "legacy agents\n" - - -def test_setup_workspace_with_opencode_loop_installs_global_assets( - tmp_path, monkeypatch, runner -): - workspace_dir = tmp_path / "workspace" - opencode_home = tmp_path / "opencode-home" - captured = {} - - monkeypatch.setenv("OPENCODE_HOME", str(opencode_home)) - monkeypatch.setattr( - "chattool.setup.opencode.ensure_nodejs_requirement", - lambda interactive, can_prompt, log_level="INFO": None, - ) - monkeypatch.setattr( - "chattool.setup.opencode.should_install_global_npm_package", - lambda *args, **kwargs: False, - ) - monkeypatch.setattr( - "chattool.setup.opencode_chatloop.run_npm_command", - lambda args, cwd=None: captured.update({"args": args, "cwd": cwd}) - or type("Result", (), {"returncode": 0, "stdout": "", "stderr": ""})(), - ) - - result = runner.invoke( - cli, - [ - "setup", - "workspace", - str(workspace_dir), - "-I", - "--with-opencode-loop", - ], - ) - - assert result.exit_code == 0 - assert not (workspace_dir / ".opencode" / "opencode.jsonc").exists() - assert (opencode_home / "plugins" / "chatloop" / "index.ts").exists() - assert (opencode_home / "plugins" / "chatloop" / "package.json").exists() - assert (opencode_home / "command" / "chatloop.md").exists() - assert (opencode_home / "command" / "chatloop-project.md").exists() - assert (opencode_home / "command" / "chatloop-status.md").exists() - config = (opencode_home / "opencode.json").read_text(encoding="utf-8") - assert (opencode_home / "plugins" / "chatloop").resolve().as_uri() in config - assert captured["args"] == ["install", "--omit=dev", "--no-audit", "--no-fund"] - assert captured["cwd"] == (opencode_home / "plugins" / "chatloop") - agents = (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") - readme = (workspace_dir / "README.md").read_text(encoding="utf-8") - memory = (workspace_dir / "MEMORY.md").read_text(encoding="utf-8") - reference = (workspace_dir / "reference" / "README.md").read_text(encoding="utf-8") - theme = (workspace_dir / "docs" / "themes" / "changelog.md").read_text( - encoding="utf-8" - ) - assert "当前 workspace 已启用 OpenCode loop-aware 模式" in agents - assert "显式触发 `/chatloop ...`" in readme - assert "项目根目录:`projects/`" in memory - assert "跨多个 project" in reference - assert "CHANGELOG.md" in theme - assert "OpenCode home:" in result.output - assert ".opencode/ directory" in result.output + assert (workspace_dir / "AGENTS.md").read_text(encoding="utf-8") == "legacy agents\n" + assert not (workspace_dir / "README.md").exists()