diff --git a/.Knowledge/manifest-routing.json b/.Knowledge/manifest-routing.json index 1f92474..fe71e89 100644 --- a/.Knowledge/manifest-routing.json +++ b/.Knowledge/manifest-routing.json @@ -1,5 +1,5 @@ { - "version": "3.8.1", + "version": "3.8.3", "knowledgeRoot": ".Knowledge", "matcherKey": "matcherId", "sourceOfTruth": ".Knowledge/manifest-routing.json", @@ -74,7 +74,7 @@ "topics": [ "f2s-task" ], - "summary": "任务清单 .task 目录的创建、续作与归档规则" + "summary": "任务清单 .task 目录的创建、续作、归档与跨机同步规则" }, { "task": "req-plan", diff --git "a/.Knowledge/stock-docs/Flow2Spec-\344\273\273\345\212\241\346\270\205\345\215\225\344\270\216\345\217\230\346\233\264\350\277\275\350\270\252.md" "b/.Knowledge/stock-docs/Flow2Spec-\344\273\273\345\212\241\346\270\205\345\215\225\344\270\216\345\217\230\346\233\264\350\277\275\350\270\252.md" index 854207e..6b19441 100644 --- "a/.Knowledge/stock-docs/Flow2Spec-\344\273\273\345\212\241\346\270\205\345\215\225\344\270\216\345\217\230\346\233\264\350\277\275\350\270\252.md" +++ "b/.Knowledge/stock-docs/Flow2Spec-\344\273\273\345\212\241\346\270\205\345\215\225\344\270\216\345\217\230\346\233\264\350\277\275\350\270\252.md" @@ -50,7 +50,9 @@ ### Git 与跨电脑续作 -`.gitignore` 使用 `.task/completed/` 与 `.task/*/completed/`,仅忽略两种布局的已完成归档。进行中的 `active/`、`todo.json` 与任务上下文可随代码提交、推送,在另一台电脑拉取后续作;两端应保持相同 `collaboration.developerId`(或相同的 Git 身份解析结果)。`init` 会将旧的整目录 `.task/` 忽略规则迁为上述两条规则。 +`collaboration.syncActiveTasks=true`(默认)时,`.gitignore` 使用 `.task/completed/` 与 `.task/*/completed/`,仅忽略两种布局的已完成归档;进行中的 `active/`、`todo.json` 与任务上下文可随代码提交、推送,在另一台电脑拉取后续作。`false` 时整个 `.task/` 留在本地。`init` 会按开关维护 `.gitignore`,并迁移旧的整目录 `.task/` 规则。 + +跨机续作要求两端解析出相同 `developerId`;共享仓库中推荐两端使用相同 `git config user.email`,不要把个人 id 写进受跟踪的 `flow2spec.config.json`。`flow2spec doctor` 会显示解析来源与 `TASK_ROOT`。提交前必须检查 `user-todos.md` / `context.md`,不得写入密码、token、私钥、数据库连接串或内网地址;归档已提交任务时,把 Git 显示的删除一并提交。 ### 3.1 `user-todos.md` 与 `acceptance.md`:用户侧两份文件,职责分离 diff --git a/.Knowledge/topics/f2s-task.md b/.Knowledge/topics/f2s-task.md index bb1e1f3..151aac6 100644 --- a/.Knowledge/topics/f2s-task.md +++ b/.Knowledge/topics/f2s-task.md @@ -1,7 +1,7 @@ --- id: f2s-task -revision: 1 -summary: "任务清单 .task 目录的创建、续作与归档规则" +revision: 2 +summary: "任务清单 .task 目录的创建、续作、归档与跨机同步规则" primary: policy confidence: manual --- @@ -51,9 +51,11 @@ TASK_ROOT/ ← `.task` 或 `.task/` ## Git 与跨电脑续作 -- `.gitignore` 使用 `.task/completed/` 与 `.task/*/completed/`,忽略单人及多人布局的已完成归档。 +- `collaboration.syncActiveTasks=true`(默认):`.gitignore` 仅使用 `.task/completed/` 与 `.task/*/completed/`,进行中任务可随 Git 同步;`false` 时整个 `.task/` 留在本地。 - `active/`、`todo.json` 与任务上下文可提交并推送;另一台电脑拉取后,使用相同 developerId 解析到原 TASK_ROOT 续作。 -- `init` 自动将旧的整目录 `.task/` 忽略规则迁为上述两条规则。 +- 跨机身份要求两端解析一致,推荐两端使用相同 `git config user.email`;`flow2spec doctor` 显示当前来源与 TASK_ROOT。 +- 提交前检查 `user-todos.md` / `context.md` 是否含凭据或内网信息;归档产生的删除 diff 与任务提交一并同步。 +- `init` 按 `syncActiveTasks` 维护 `.gitignore`,并迁移旧的整目录 `.task/` 规则。 ## 跨会话续作 diff --git a/.Knowledge/topics/flow2spec-collaboration.md b/.Knowledge/topics/flow2spec-collaboration.md index f8aa31d..ca4b838 100644 --- a/.Knowledge/topics/flow2spec-collaboration.md +++ b/.Knowledge/topics/flow2spec-collaboration.md @@ -1,6 +1,6 @@ --- id: flow2spec-collaboration -revision: 2 +revision: 3 summary: "任务按身份隔离与跨电脑续作、知识 delta 合入及 revision 冲突处理" dependsOn: [f2s-task] primary: feature @@ -17,9 +17,10 @@ tags: [policy] ## 协作边界 -- `.task//` 保存个人 checklist、会话上下文与用户代办;进行中任务可随代码提交,completed 归档由 `.task/completed/` 与 `.task/*/completed/` 忽略;Agent 只读写当前 `TASK_ROOT`。 +- `.task//` 保存个人 checklist、会话上下文与用户代办;`collaboration.syncActiveTasks=true`(默认)时进行中任务可随代码提交,completed 归档由 `.task/completed/` 与 `.task/*/completed/` 忽略;`false` 时整个 `.task/` 留在本地;Agent 只读写当前 `TASK_ROOT`。 - `.Knowledge/` 保存团队共享事实,随代码通过 Git / PR 评审。 - `collaboration.enabled` 只控制任务根是否按 developerId 分层,不关闭知识库协作。 +- 跨机续作要求两端解析出相同 developerId;推荐两端使用相同 `git config user.email`,共享仓库不要往受跟踪的 `flow2spec.config.json` 写个人 id。 ## 知识合入 diff --git a/README.en.md b/README.en.md index 06ac6a1..279ca38 100644 --- a/README.en.md +++ b/README.en.md @@ -62,11 +62,11 @@ Flow2Spec does not add documentation for its own sake. It keeps a small, machine | Topic shards | Stores project facts such as APIs, limits, locks, data rules, and workflows. | `.Knowledge/topics/*.md` | | Agent entrypoints | Installs rules and skills for the selected AI coding clients. | client configuration roots, `.dsh/`, `AGENTS.md` | | Skill workflows | Clarifies requirements, writes specs, implements, fixes, syncs knowledge, and commits. | `f2s-*` skills | -| Team collaboration | Keeps each developer's task state local while merging reviewed knowledge through structured deltas and topic revisions. | `.task//`, `.Knowledge/` | +| Team collaboration | In-progress tasks can travel with the code for continuation on another computer, while completed archives stay local; reviewed knowledge is merged through structured deltas and topic revisions. | `.task//`, `.Knowledge/` | ## Built for shared repositories -Flow2Spec separates collaboration state by ownership. Checklists, session context, and user todos stay under each developer's local `TASK_ROOT` and do not enter Git. Confirmed project knowledge remains shared in `.Knowledge/`. +Flow2Spec separates collaboration state by ownership. Checklists, session context, and user todos live under each developer's `TASK_ROOT`; with `collaboration.syncActiveTasks=true` (the default), in-progress tasks can be committed with the code while `completed/` archives stay local. Confirmed project knowledge remains shared in `.Knowledge/`. Knowledge-producing skills write a structured `kb-delta.json` instead of editing topic files directly. Before apply, the CLI compares the delta's `baseRevisions` with the topic revisions on disk. Different topics can merge independently; concurrent changes to the same topic stop for a semantic review after the latest branch state is pulled. diff --git a/README.md b/README.md index e21954e..b95aeb2 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,11 @@ Flow2Spec 不是为了增加文档数量。它把项目事实保存在一层小 | 主题分片 | 保存 API、上限、锁、数据规则、业务流程等项目事实。 | `.Knowledge/topics/*.md` | | Agent 入口 | 为选中的 AI 编程客户端安装规则和技能。 | 客户端配置根、`.dsh/`、`AGENTS.md` | | 技能工作流 | 澄清需求、编写方案、实现、修复、同步知识、提交。 | `f2s-*` skills | -| 团队协作 | 每个人的任务现场留在本地,确认后的知识通过结构化 delta 与 topic revision 合入共享仓库。 | `.task//`, `.Knowledge/` | +| 团队协作 | 进行中任务可随代码同步以跨电脑续作,completed 归档留在本地;确认后的知识通过结构化 delta 与 topic revision 合入共享仓库。 | `.task//`, `.Knowledge/` | ## 多人共用一份知识库 -Flow2Spec 按所有权拆分协作状态。checklist、会话上下文和用户代办保存在每名开发者自己的 `TASK_ROOT`,默认不进 Git;已经确认的项目知识统一进入 `.Knowledge/`。 +Flow2Spec 按所有权拆分协作状态。checklist、会话上下文和用户代办保存在每名开发者自己的 `TASK_ROOT`;`collaboration.syncActiveTasks=true`(默认)时进行中任务可随代码提交,`completed/` 归档留在本地;已经确认的项目知识统一进入 `.Knowledge/`。 知识类技能先生成结构化 `kb-delta.json`,不直接改 topic。真正 apply 前,CLI 会比较 delta 的 `baseRevisions` 与磁盘上的 topic revision。修改不同 topic 可以分别合入;两个人同时修改同一 topic 时,后合入的一方需要先拉取最新版本、重读语义,再改写 delta。 diff --git "a/docs/Flow2Spec\345\237\272\347\241\200\344\273\213\347\273\215.md" "b/docs/Flow2Spec\345\237\272\347\241\200\344\273\213\347\273\215.md" index 622e496..ebc5ae9 100644 --- "a/docs/Flow2Spec\345\237\272\347\241\200\344\273\213\347\273\215.md" +++ "b/docs/Flow2Spec\345\237\272\347\241\200\344\273\213\347\273\215.md" @@ -270,7 +270,7 @@ flowchart TD 图 3:任务分开,知识合流 -`.task/` 默认不进 Git,并按 `developerId` 分成不同的 `TASK_ROOT`。Alice 的 Agent 不会为了找续作任务去扫描 Bob 的目录。这里保存的是 checklist、会话上下文和用户代办,它们属于执行者自己的工作现场。 +`.task/` 默认只把 `completed/` 归档留在本地,进行中任务可按 `collaboration.syncActiveTasks` 随代码同步,用于同一开发者跨电脑续作;目录仍按 `developerId` 分成不同的 `TASK_ROOT`。Alice 的 Agent 不会为了找续作任务去扫描 Bob 的目录。这里保存的是 checklist、会话上下文和用户代办,它们属于执行者自己的工作现场。 `.Knowledge/` 则必须全员共享。技能先把知识变更写成结构化 delta,里面带着读取时的 topic revision;真正写入前由 `flow2spec kb plan` 检查磁盘版本。两个人改不同 topic 可以各自推进,改到同一个 topic 时,后合入的一方必须拉取最新版本、重新理解语义,再更新 delta。 diff --git a/docs/en/Flow2Spec-Introduction.md b/docs/en/Flow2Spec-Introduction.md index ac9ba04..8f8da1e 100644 --- a/docs/en/Flow2Spec-Introduction.md +++ b/docs/en/Flow2Spec-Introduction.md @@ -288,7 +288,7 @@ flowchart LR Figure 3: Tasks stay separate; knowledge converges -`.task/` is ignored by Git by default and split into developer-specific `TASK_ROOT` directories. `.Knowledge/` is shared. Skills express changes as structured deltas with the topic revisions they read; `flow2spec kb plan` checks those revisions before disk writes. Different topics can advance independently. When two people change the same topic, the second person pulls, rereads the latest meaning, and rewrites the delta. +`.task/` keeps only `completed/` archives local by default; in-progress tasks can travel through Git (controlled by `collaboration.syncActiveTasks`) for the same developer's cross-computer continuation, and remain split into developer-specific `TASK_ROOT` directories. `.Knowledge/` is shared. Skills express changes as structured deltas with the topic revisions they read; `flow2spec kb plan` checks those revisions before disk writes. Different topics can advance independently. When two people change the same topic, the second person pulls, rereads the latest meaning, and rewrites the delta. This is not automatic text concatenation. It isolates state that should remain personal, keeps shared facts in Git, and stops where a semantic decision is needed. See [Team Collaboration](./team-collaboration.md). diff --git a/docs/en/architecture.md b/docs/en/architecture.md index 5340427..6798a96 100644 --- a/docs/en/architecture.md +++ b/docs/en/architecture.md @@ -98,16 +98,16 @@ The documentation curation chain produces two document types: Flow2Spec assumes that a team shares one knowledge base. It applies opposite policies to two loops: **task state is separated; knowledge is merged**. -- **Task loop**: `.task/` stays out of Git and is partitioned by `/` as personal working state. +- **Task loop**: `.task/` is partitioned by ``. With `collaboration.syncActiveTasks=true` (default), only `completed/` stays out of Git while in-progress tasks can travel with the code for the same developer's cross-computer continuation; with `false`, the whole directory stays local. - **Knowledge loop**: `.Knowledge/` stays in Git. Topic-level integer `revision` values act as optimistic disk locks, while `kb-delta.json` carries structured changes into the shared knowledge base. -Both policies follow the same rule: team facts belong in Git; personal execution state does not. +Both policies follow the same rule: team facts belong in Git; personal execution state may travel for continuity, but it is not team truth. ### 6.1 Task loop: a local TASK_ROOT per developer With collaboration enabled, each developer gets `.task//`. The task rule creates, resumes, and archives tasks only under the current `TASK_ROOT`; it must not scan another developer's `todo.json`. If collaboration is disabled or no identity is available, Flow2Spec retains the legacy `.task/` root. -Because `.task/` is local, team progress should be read from PRs, commits, `.Knowledge/` diffs, and milestones, not from another person's active checklist. +With `collaboration.syncActiveTasks=true`, `active/`, `todo.json`, and `kb-delta.json` can be committed with the code; `completed/` stays local. Both machines must resolve the same `developerId` (preferably the same `git config user.email`); `flow2spec doctor` prints the resolved source and `TASK_ROOT`. Team progress should still be read from PRs, commits, `.Knowledge/` diffs, and milestones, not from task files. ### 6.2 Knowledge loop: structured deltas and optimistic locking @@ -125,7 +125,7 @@ This is a topic-level lock. Two unrelated edits to different sections of the sam ### 6.3 Why the boundary is drawn here -Sharing `.task/` would turn rapidly changing checklists and session context into repository noise. Splitting `.Knowledge/` by developer would create competing versions of project truth. Keeping the first local and the second reviewable gives each kind of state one owner and one lifecycle. See [Team Collaboration](./team-collaboration.md) for the complete workflow and conflict examples. +Treating task state as a team board would turn rapidly changing checklists and session context into repository noise. Splitting `.Knowledge/` by developer would create competing versions of project truth. Keeping `completed/` local, syncing only in-progress tasks for the same developer, and keeping knowledge reviewable gives each kind of state one owner and one lifecycle. See [Team Collaboration](./team-collaboration.md) for the complete workflow and conflict examples. --- diff --git a/docs/en/commands-reference.md b/docs/en/commands-reference.md index c53b040..0a85504 100644 --- a/docs/en/commands-reference.md +++ b/docs/en/commands-reference.md @@ -703,7 +703,7 @@ It checks: - whether `flow2spec.config.json`, root `AGENTS.md`, and `.Knowledge/manifest-routing.json` exist and are usable - whether detected `.codex`, `.claude`, and `.cursor` roots contain their required entry and hook files - the resolved `developerId`, source, and `TASK_ROOT`; falling back to the legacy `.task/` root produces a warning -- whether the root `.gitignore` ignores `.task/completed/` and `.task/*/completed/` while allowing active tasks to be committed +- whether the root `.gitignore` matches `collaboration.syncActiveTasks`: `true` ignores only `.task/completed/` and `.task/*/completed/` while allowing active tasks to be committed; `false` ignores the whole `.task/` - strict knowledge graph health, topic revisions, and routing metadata drift Human-readable output marks each check with `[PASS]`, `[WARN]`, or `[FAIL]` and includes a repair suggestion when applicable. `--json` returns a stable `ok / package / cwd / summary / checks` structure for CI and scripts. diff --git a/docs/en/design-principles.md b/docs/en/design-principles.md index 71c025b..c5f70a3 100644 --- a/docs/en/design-principles.md +++ b/docs/en/design-principles.md @@ -157,11 +157,13 @@ Old .task/ -> Git -> everyone shares one todo.json Current - .task/alice/ -> Alice resumes locally + .task/alice/ -> Alice resumes locally; with syncActiveTasks=true, active/todo can travel through Git .task/bob/ -> Bob resumes locally .Knowledge/ -> Git -> shared team facts ``` +Cross-computer continuation does not return to a single shared `todo.json`: the `developerId` partition remains, only in-progress tasks and `todo.json` can be synced, `completed/` stays local, and both machines must resolve the same `developerId`. The behavior is controlled by `collaboration.syncActiveTasks` (default on; it can be turned off for local-only state). + The current developer's `TASK_ROOT` owns progress. Code, formal documents, and `.Knowledge/` own the delivered capability. `developerId` remains useful on shared machines and as an Agent read boundary; it is not a remote identity or authorization system. ### 6. Why Knowledge Merges Use Optimistic Locks diff --git a/docs/en/directory-conventions.md b/docs/en/directory-conventions.md index d60f5bf..0663f16 100644 --- a/docs/en/directory-conventions.md +++ b/docs/en/directory-conventions.md @@ -24,14 +24,14 @@ See [architecture.md §1](./architecture.md) for Memory Coding four rings. | `.Knowledge/manifest-routing.json` | **L0** Machine-readable routing skeleton (task/topic/`topicDependencies`/`topicMetadata`) | | `.Knowledge/matchers/*.json` | **L1** Keyword fragments (`id` plus `includeAny`/`includeAll` qualifiers and `excludeAny`/`excludeAll` vetoes); **match** reads one shard via `matcherPath` | | `.Knowledge/migration-report.md` | Migration comparison table and deletion path list written by the historical `f2s-kb-migrate` skill (removed from the package; existing files may be kept) | -| `.task/` | Local change-tracking state, ignored by Git by default. In collaboration mode it has an extra `/` layer; see “The two `.task/` layouts” below. It is created only when `changeTracking.*` is `true` or `f2s-req-plan` is explicitly invoked. | +| `.task/` | Change-tracking task state. With `collaboration.syncActiveTasks=true` (default), only `.task/completed/` and `.task/*/completed/` are ignored, so in-progress tasks can travel through Git; with `false`, the whole directory stays local. In collaboration mode it has an extra `/` layer; see “The two `.task/` layouts” below. It is created only when `changeTracking.*` is `true` or `f2s-req-plan` is explicitly invoked. | | `Config Root/rules/` | Rule files (Cursor `.mdc`, Claude `.md`) | | `Config Root/skills/` | Skill definitions (`SKILL.md`) | | `Config Root/template/` | (Deprecated) No longer written to; historical directories may be cleaned up | | `.codex/AGENTS.md` | Codex unified entry point and loading instructions | | `.dsh/AGENTS.md` | DeepSeek Harness directory pointer; the complete project entry remains root `AGENTS.md` | | `.dsh/topics/` | Long-form rule mirrors loaded on demand by DeepSeek Harness | -| `flow2spec.config.json` | Project root configuration, controls `subAgent`, `switchAgentVerification`, `changeTracking` (nested object with `feat` / `fix` / `implement` sub-items) | +| `flow2spec.config.json` | Project root configuration, controls `subAgent`, `switchAgentVerification`, `changeTracking` (nested object with `feat` / `fix` / `implement` sub-items), and `collaboration.syncActiveTasks` | > See [Usage Guide Section 1](./usage-guide.md) for multi-platform references and path tables (detail maintained in a single table); **the authoritative source remains `Read(flow2spec.config.json)`**. @@ -43,7 +43,7 @@ See [architecture.md §1](./architecture.md) for Memory Coding four rings. 2. `f2s-kb-build` reads from `.Knowledge/stock-docs` and updates `.Knowledge/topics`, `.Knowledge/index.md`, `.Knowledge/manifest-routing.json`, `.Knowledge/matchers/*.json`. 3. Implementation tasks uniformly read from `.Knowledge/req-docs/*.md`. 4. `manifest-routing.json` and `matchers/*.json` are maintained by `f2s-*` skill workflows; `.Knowledge/manifest-matchers.json` is no longer used (`flow2spec init` will delete legacy files). -5. `.task/` and `.Knowledge/update-check.json` are local runtime state and should not be committed by default; `flow2spec init` non-destructively adds the corresponding `.gitignore` entries. Task progress is a personal working draft; the knowledge base is the shared team record. +5. `.task/` is task runtime state whose Git boundary is controlled by `collaboration.syncActiveTasks`: `true` (default) ignores only completed archives and allows in-progress tasks to travel with the code; `false` keeps the whole directory local. `flow2spec init` maintains the corresponding `.gitignore` entries non-destructively. Task progress is a personal working draft; the knowledge base is the shared team record. --- @@ -58,6 +58,8 @@ The layout depends on the resolved `collaboration` settings in `flow2spec.config Both layouts use the same child-directory convention: active tasks use the task name; archived tasks use **`-`**. +`syncActiveTasks` does not change `TASK_ROOT` itself: when `true`, `active/`, `todo.json`, and `kb-delta.json` can be committed and `completed/` is ignored; when `false`, the whole `.task/` stays local. Cross-computer continuation also requires both machines to resolve the same `developerId` (preferably the same `git config user.email`). + ### How developerId is resolved Flow2Spec takes the first usable value in this order: @@ -71,7 +73,7 @@ For Git-derived identities, a non-ASCII value becomes a stable `dev-/`; continuation logic reads only the current `TASK_ROOT`. +- `.task/` is task runtime state. With `collaboration.syncActiveTasks=true` (default), in-progress tasks can be committed while `completed/` stays local; with `false`, the whole directory stays local. Collaboration mode partitions it into `.task//`, and continuation logic reads only the current `TASK_ROOT`. - `.Knowledge/` is shared through Git. Knowledge skills produce a structured `kb-delta.json`, and the CLI checks topic revisions before merging it. -developerId resolution follows explicit `collaboration.developerId` -> Git email prefix -> Git user name -> legacy `.task/`. Invalid explicit ids fail instead of silently changing identity. Non-ASCII Git identities use a stable `dev-xxxxxxxx` hash fallback with a warning. Setting `collaboration.enabled: false` always selects the legacy root. +developerId resolution follows explicit `collaboration.developerId` -> Git email prefix -> Git user name -> legacy `.task/`. Invalid explicit ids fail instead of silently changing identity. Non-ASCII Git identities use a stable `dev-xxxxxxxx` hash fallback with a warning. Setting `collaboration.enabled: false` always selects the legacy root. For cross-computer continuation, use the same `git config user.email` on both machines so the resolved `TASK_ROOT` matches; `flow2spec doctor` prints the source. The knowledge merge commands are: diff --git "a/docs/\344\275\223\347\263\273\344\270\216\345\216\237\347\220\206.md" "b/docs/\344\275\223\347\263\273\344\270\216\345\216\237\347\220\206.md" index bcf2132..adb00ab 100644 --- "a/docs/\344\275\223\347\263\273\344\270\216\345\216\237\347\220\206.md" +++ "b/docs/\344\275\223\347\263\273\344\270\216\345\216\237\347\220\206.md" @@ -100,22 +100,25 @@ Flow2Spec 的目标是把"业务知识沉淀"与"Agent 能力加载"拆开,并 Flow2Spec 假设团队场景是「一份知识库、多个人一起写」。围绕这个假设,仓内两条环走了相反的策略:**任务环切开、知识环合并**。 -- **任务环**:`.task/` 不进 Git、按 `/` 分层,是个人过程草稿。 +- **任务环**:`.task/` 按 `/` 分层;默认(`collaboration.syncActiveTasks=true`)仅把 `completed/` 留在本地,进行中任务可随代码同步,用于同一开发者的跨机续作。 - **知识环**:`.Knowledge/` 完整走 Git,配合 topic frontmatter 里的整数 `revision` 做**磁盘乐观锁**,多人并写靠 `kb-delta.json` 结构化提交自动合并。 -两条线共同用一个前提——Git 是团队的共享通道。凡是团队要看的东西都进 Git,凡是个人过程状态都不进。 +两条线共同用一个前提——Git 是团队的共享通道。团队事实进 Git;个人过程状态可以为了续作而在 Git 中携带,但不作为团队事实。 ### 6.1 任务环:按 developerId 本地隔离 -`.task/` 记的是「这一轮 AI 会话在做什么」——checklist、user-todos、跨会话续作的中间态。这些内容对别人来说是噪声,所以: +`.task/` 记的是「这一轮 AI 会话在做什么」——checklist、user-todos、跨会话续作的中间态。这些内容对别人来说是噪声,所以默认不作为团队看板;但同一开发者换电脑时需要它们: -- `.task/` 默认写进 `.gitignore`,不跨机器同步; +- `collaboration.syncActiveTasks: true`(默认)时,`.gitignore` 仅忽略 `.task/completed/` 与 `.task/*/completed/`;`active/`、`todo.json`、`kb-delta.json` 可与代码一起提交; +- `collaboration.syncActiveTasks: false` 时,整个 `.task/` 留在本地,不进入 Git; - `collaboration.enabled: true`(默认)时,`.task/` 内部再按 `/` 分层,兜底同机多账号、共享构建机等场景; - developerId 从 `collaboration.developerId` → git email → git name 逐档降级;解析不出(或 `enabled: false`)时回落到 legacy 单根 `.task/`,与旧版行为一致。 +跨机续作要求两端解析出**相同 developerId**;推荐两端设置相同 `git config user.email`,`flow2spec doctor` 会显示解析来源与 `TASK_ROOT`。 + 看到 `.task//` 是隔离模式,看到 `.task/todo.json` 直接躺根下是 legacy——两种形态并存。详细路径与 developerId 解析规则见 [目录与路径约定 §`.task/` 的两种形态](./目录与路径约定.md)。 -**这条切法的副作用**:manager 视角看团队进度不能靠 `.task/completed/`(它不在 Git 里),要走 git log、PR、里程碑文档。这是刻意的取舍——`.task/` 是过程草稿,`.Knowledge/`、commit message 与 milestone 才是团队产出。 +**这条切法的副作用**:manager 视角不能把 `.task/` 当团队看板——`completed/` 不在 Git 里,进行中任务也可能只存在于个人分支或尚未提交。团队进度仍要看 git log、PR、`.Knowledge/` diff 与里程碑文档。这是刻意的取舍——`.task/` 是过程草稿,`.Knowledge/`、commit message 与 milestone 才是团队产出。 ### 6.2 知识环:磁盘乐观锁 + 结构化 delta @@ -134,7 +137,7 @@ Flow2Spec 假设团队场景是「一份知识库、多个人一起写」。围 ### 6.3 两条线为什么这样切 -一个反问:既然 `.task/` 不进 Git,为什么还要 `/` 这一层?在 [目录与路径约定 §`.task/` 的两种形态](./目录与路径约定.md) 里列了三个具体边界,简言之是「本地磁盘隔离不能覆盖所有场景」(同机多账号、AI 越界扫描、未来 `completed/` 白名单进 Git 的可能性)。 +一个反问:既然 `.task/` 默认只同步进行中任务、`completed/` 留在本地,为什么还要 `/` 这一层?在 [目录与路径约定 §`.task/` 的两种形态](./目录与路径约定.md) 里列了三个具体边界,简言之是「本地磁盘隔离不能覆盖所有场景」(同机多账号、AI 越界扫描,以及同一开发者跨机器时的路径稳定性)。 反过来 `.Knowledge/` 也可以走「按 developerId 拆知识库」的路线——但那会导致每个人看到的知识库版本不一样,团队协作的语义直接崩掉。所以知识库必须共享,冲突必须解决,只是解决方式换成了「自动合并 + 冲突时人工介入」。 diff --git "a/docs/\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/docs/\344\275\277\347\224\250\350\257\264\346\230\216.md" index 8a2d9d6..0b30e13 100644 --- "a/docs/\344\275\277\347\224\250\350\257\264\346\230\216.md" +++ "b/docs/\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -136,9 +136,9 @@ Claude 项目执行 `flow2spec init claude` 后会写入 `.claude/settings.json` ### 多人协作 -Flow2Spec 把协作拆成两条互不干扰的线:**`.Knowledge/` 走 Git,全员共享**;**`.task/` 只在本地,按 developerId 分层**。团队沉淀和个人过程分开管,不用为一份 `todo.json` 反复解冲突。 +Flow2Spec 把协作拆成两条互不干扰的线:**`.Knowledge/` 走 Git,全员共享**;**`.task/` 按 developerId 分层,进行中任务可选随 Git 同步、completed 留在本地**。团队沉淀和个人过程分开管,不用为一份 `todo.json` 反复解冲突。 -### 任务进度:本地按人隔离 +### 任务进度:按人隔离,可选跨机同步 最小配置: @@ -146,12 +146,15 @@ Flow2Spec 把协作拆成两条互不干扰的线:**`.Knowledge/` 走 Git, { "collaboration": { "enabled": true, - "developerId": "" + "developerId": "", + "syncActiveTasks": true } } ``` -`developerId` 留空时按 git identity 兜底解析;非 ASCII 的 git identity 会退化到 `dev-` 的稳定 hash id,同时在解析上下文里挂 `warnings` 提醒改成显式英文 id——不会静默降到 legacy,也不会因为字符集炸掉。解析优先级、hash 兜底与迁移步骤详见 [目录与路径约定 § `.task/` 的两种形态](./目录与路径约定.md)。 +`syncActiveTasks=true`(默认)时只忽略 `.task/completed/` 与 `.task/*/completed/`,`active/`、`todo.json`、`kb-delta.json` 可随代码提交;`false` 时整个 `.task/` 留在本地。 + +`developerId` 留空时按 git identity 兜底解析;非 ASCII 的 git identity 会退化到 `dev-` 的稳定 hash id,同时在解析上下文里挂 `warnings` 提醒改成显式英文 id——不会静默降到 legacy,也不会因为字符集炸掉。跨机续作要求两端解析出相同 id,推荐两端使用相同 `git config user.email`;`flow2spec doctor` 会显示解析来源。解析优先级、hash 兜底与迁移步骤详见 [目录与路径约定 § `.task/` 的两种形态](./目录与路径约定.md)。 执行约束是硬的:`rules/f2s-task` 只准读写当前 developer 的 `TASK_ROOT`,禁止扫别人 `.task//` 目录,也不会自动跨人合并 `todo.json`。 @@ -182,7 +185,7 @@ flow2spec kb apply .task//active//kb-delta.json # 落盘 git add .Knowledge/ && git commit && git push ``` -`.task/` 全程留在本地,不进 push;`.Knowledge/` 每次 apply 之后再提交。只要两个人基于同一个 `revision` 修改同一 topic,先合入的一方就会推进版本,另一方在拉取后重新 plan 时会被阻塞,即使双方碰的是不同段落。锁是 topic 级,不是行级。 +`collaboration.syncActiveTasks=true` 时进行中任务与 `todo.json` 可随代码提交(`completed/` 归档仍留在本地);`false` 时 `.task/` 全程留在本地,不进 push。`.Knowledge/` 每次 apply 之后再提交。只要两个人基于同一个 `revision` 修改同一 topic,先合入的一方就会推进版本,另一方在拉取后重新 plan 时会被阻塞,即使双方碰的是不同段落。锁是 topic 级,不是行级。 完整协作模型、同 topic 冲突示例与管理者视角见 [团队协作](./团队协作.md)。 diff --git "a/docs/\345\221\275\344\273\244\350\257\264\346\230\216.md" "b/docs/\345\221\275\344\273\244\350\257\264\346\230\216.md" index bc6a031..685325a 100644 --- "a/docs/\345\221\275\344\273\244\350\257\264\346\230\216.md" +++ "b/docs/\345\221\275\344\273\244\350\257\264\346\230\216.md" @@ -821,7 +821,7 @@ flow2spec doctor --json - `flow2spec.config.json`、根 `AGENTS.md` 与 `.Knowledge/manifest-routing.json` 是否存在且可用 - 已存在的 `.codex`、`.claude`、`.cursor` 配置根是否包含必要入口和 hooks - 实际解析出的 `developerId`、来源与 `TASK_ROOT`;无法识别身份而回退到 `.task/` 时给出警告 -- 根 `.gitignore` 是否仅忽略 `.task/completed/` 与 `.task/*/completed/`,保留进行中任务可提交 +- 根 `.gitignore` 是否按 `collaboration.syncActiveTasks` 对齐:`true` 时仅忽略 `.task/completed/` 与 `.task/*/completed/`,保留进行中任务可提交;`false` 时忽略整个 `.task/` - 知识图、topic revision 和 routing 元数据是否通过严格校验 文本输出用 `[PASS]`、`[WARN]`、`[FAIL]` 标记每项,并在异常项后给出修复建议。`--json` 输出稳定的 `ok / package / cwd / summary / checks` 结构,便于 CI 或脚本消费。 diff --git "a/docs/\345\233\242\351\230\237\345\215\217\344\275\234.md" "b/docs/\345\233\242\351\230\237\345\215\217\344\275\234.md" index a4cdabb..772b81a 100644 --- "a/docs/\345\233\242\351\230\237\345\215\217\344\275\234.md" +++ "b/docs/\345\233\242\351\230\237\345\215\217\344\275\234.md" @@ -4,9 +4,11 @@ Flow2Spec 按开发者隔离个人任务现场;进行中的任务可随代码 对应到目录就是一句话: -- `.task/` 保存个人过程;`active/` 与 `todo.json` 可提交,completed 归档默认忽略; +- `.task/` 保存个人过程;`collaboration.syncActiveTasks=true`(默认)时 `active/` 与 `todo.json` 可提交、completed 归档忽略;`false` 时整个目录留在本地; - `.Knowledge/` 是团队知识,和代码一起评审、提交、拉取。 +跨机续作的前提是两端解析出相同 `developerId`。共享仓库里优先让两端使用相同 `git config user.email`,不要往受跟踪的 `flow2spec.config.json` 写个人 id;单人仓库才适合显式配置 `collaboration.developerId`。`flow2spec doctor` 会显示当前解析来源与 `TASK_ROOT`。 + 这条边界比具体命令更重要。边界立住以后,`developerId`、`kb-delta.json` 和 topic revision 都只是让它可执行的机制。 --- @@ -225,10 +227,10 @@ revision 是磁盘乐观锁,不是远端服务上的全局锁。它不会隔 不需要为 Flow2Spec 单独设计一套复杂流程。下面几条约定已经足够覆盖大多数团队: -- 每个人配置稳定、可读的 `collaboration.developerId`,共享机器上尤其如此; +- 每个人在两端使用一致的 git 身份(推荐相同 `git config user.email`),共享仓库不要往 `flow2spec.config.json` 写个人 `developerId`;单人仓库可显式配置; - 开工先拉取,知识变更和对应实现放在同一个 PR 或一组能互相追溯的提交里; - topic 按单一职责拆分。topic 过大不仅影响路由,也会扩大 revision 冲突面; -- `.gitignore` 使用 `.task/completed/` 与 `.task/*/completed/`;进行中任务可提交,`todo.json` 用于个人续作; +- `collaboration.syncActiveTasks=true` 时 `.gitignore` 使用 `.task/completed/` 与 `.task/*/completed/`,进行中任务可提交,`todo.json` 用于个人续作;`false` 时整个 `.task/` 留在本地; - topic 正文尽量走 `f2s-kb-*` 形成 delta。直接手改可以被 Git 记录,却绕过了 revision 预检; - 合并前看 `.Knowledge/` diff,不只看 `kb plan` 的“mergeable”。结构可合并不代表业务表述一定正确; - 团队统一 Flow2Spec 版本。CLI schema 和模板能力不一致时,协作问题很难靠流程约定补救。 @@ -263,7 +265,11 @@ topic 已经大到频繁互相阻塞时,先判断它是否混进了多个独 ### 换一台电脑后,原来的 active 任务会自动出现吗? -将进行中任务和 `todo.json` 提交、推送,再在另一台电脑拉取即可。两端应配置相同 `collaboration.developerId`,或确保 Git 身份解析结果相同;Agent 随后按原 `TASK_ROOT` 续作。 +将进行中任务和 `todo.json` 提交、推送,再在另一台电脑拉取即可(要求 `collaboration.syncActiveTasks=true`)。两端应确保 Git 身份解析结果相同,推荐使用相同 `git config user.email`;Agent 随后按原 `TASK_ROOT` 续作。`flow2spec doctor` 可核对当前解析来源。 + +### 不想让任务文件进 Git 怎么办? + +把 `collaboration.syncActiveTasks` 设为 `false`。`flow2spec init` 会把 `.gitignore` 恢复为忽略整个 `.task/`,任务现场只在本机续作。 ### `collaboration.enabled: false` 会关闭知识库协作吗? diff --git "a/docs/\347\233\256\345\275\225\344\270\216\350\267\257\345\276\204\347\272\246\345\256\232.md" "b/docs/\347\233\256\345\275\225\344\270\216\350\267\257\345\276\204\347\272\246\345\256\232.md" index cc1f124..1f49725 100644 --- "a/docs/\347\233\256\345\275\225\344\270\216\350\267\257\345\276\204\347\272\246\345\256\232.md" +++ "b/docs/\347\233\256\345\275\225\344\270\216\350\267\257\345\276\204\347\272\246\345\256\232.md" @@ -24,14 +24,14 @@ Memory Coding 四环总览见 [体系与原理 §1](./体系与原理.md)。 | `.Knowledge/manifest-routing.json` | **L0** 机读路由骨架(task/topic/`topicDependencies`/`topicMetadata`) | | `.Knowledge/matchers/*.json` | **L1** 关键词分片(`id` + `includeAny`/`includeAll` 资格词、`excludeAny`/`excludeAll` 否决词),由 `matcherPath` 直链;**match** 只读一片 | | `.Knowledge/migration-report.md` | 历史版本 `f2s-kb-migrate` 落盘的迁移对照表与拟删除路径列表(该技能已移除,存量文件可保留) | -| `.task/` | 变更追踪任务清单目录,本地运行态、默认进 `.gitignore`。多人协作时按 `/` 再分一层,两种形态见下文 §`.task/` 的两种形态。仅当 `changeTracking.*` 为 `true` 或显式调用 `f2s-req-plan` 时创建 | +| `.task/` | 变更追踪任务清单目录。`collaboration.syncActiveTasks=true`(默认)时仅忽略 `.task/completed/` 与 `.task/*/completed/`,进行中任务可随 Git 同步;`false` 时整个目录留在本地。多人协作时按 `/` 再分一层,两种形态见下文 §`.task/` 的两种形态。仅当 `changeTracking.*` 为 `true` 或显式调用 `f2s-req-plan` 时创建 | | `配置根/rules/` | 规则文件(Cursor `.mdc`,Claude `.md`) | | `配置根/skills/` | 技能定义(`SKILL.md`) | | `配置根/template/` | (废弃)不再写入;历史目录可清理 | | `.codex/AGENTS.md` | Codex 统一入口与加载说明 | | `.dsh/AGENTS.md` | DeepSeek Harness 目录指针;完整项目入口仍是根 `AGENTS.md` | | `.dsh/topics/` | DeepSeek Harness 按需读取的规则长文镜像 | -| `flow2spec.config.json` | 项目根配置,控制 `subAgent`、`switchAgentVerification`、`changeTracking`(嵌套对象,含 `feat` / `fix` / `implement` 三个子项) | +| `flow2spec.config.json` | 项目根配置,控制 `subAgent`、`switchAgentVerification`、`changeTracking`(嵌套对象,含 `feat` / `fix` / `implement` 三个子项)、`collaboration.syncActiveTasks` | > 多端提示与路径表见 [使用说明 § 一](./使用说明.md)(详表单点维护);**权威仍为 Read(`flow2spec.config.json`)**。 @@ -43,7 +43,7 @@ Memory Coding 四环总览见 [体系与原理 §1](./体系与原理.md)。 2. `f2s-kb-build` 从 `.Knowledge/stock-docs` 读,更新 `.Knowledge/topics`、`.Knowledge/index.md`、`.Knowledge/manifest-routing.json`、`.Knowledge/matchers/*.json`。 3. 实现类任务统一读取 `.Knowledge/req-docs/*.md`。 4. `manifest-routing.json` 与 `matchers/*.json` 由 `f2s-*` 技能流程维护;不再使用 `.Knowledge/manifest-matchers.json`(`flow2spec init` 会删除遗留文件)。 -5. `.task/` 与 `.Knowledge/update-check.json` 是本地运行态,默认不进 Git;`flow2spec init` 会非破坏式补充 `.gitignore`。任务进度是**个人过程草稿**,知识库才是**团队沉淀**,两者分线管,具体形态见下文 §`.task/` 的两种形态。 +5. `.task/` 是任务运行态,Git 边界由 `collaboration.syncActiveTasks` 控制:`true`(默认)时仅忽略 completed 归档,进行中任务可随代码同步;`false` 时整个目录留在本地。`flow2spec init` 会非破坏式维护 `.gitignore`。任务进度是**个人过程草稿**,知识库才是**团队沉淀**;同一开发者跨机续作时,进行中任务可随代码同步。 --- @@ -58,6 +58,8 @@ Memory Coding 四环总览见 [体系与原理 §1](./体系与原理.md)。 两种形态下 `active/` 与 `completed/` 的子目录命名规则一致:进行中直接用 task-name;已归档改成 **`-`**(日期在前)。 +`syncActiveTasks` 不改变 `TASK_ROOT` 本身:`true` 时 `active/`、`todo.json`、`kb-delta.json` 可提交,`completed/` 忽略;`false` 时整个 `.task/` 留在本地。跨机续作还要求两端解析出相同 `developerId`(推荐相同 `git config user.email`)。 + ### developerId 的解析顺序 自顶向下取第一个能落地的: @@ -71,11 +73,11 @@ Memory Coding 四环总览见 [体系与原理 §1](./体系与原理.md)。 ### 为什么在本地也要按人分层 -`.task/` 已经不进 Git 了,本地机器天然按人隔离,但 `/` 这一层仍然有意义,覆盖三个边界: +`.task/` 即使整体留在本地(`syncActiveTasks=false`),`/` 这一层仍然有意义,覆盖三个边界: - 同一台开发/构建机上多个账号共用(devcontainer、共享 Mac mini、跳板机) - AI 需要一个稳定的 `TASK_ROOT` 边界,防止跨读别人未归档的任务上下文(配置根 `rules/f2s-task` 是硬约束) -- 若团队日后约定把 `completed/` 白名单加回 Git 做团队归档,`/` 结构直接生效,不用回头改代码 +- 同一开发者跨机器续作时,两端需要解析到同一个 `TASK_ROOT`;`/` 让路径由身份稳定决定,而不是依赖本机目录状态 ### 从 legacy 迁到隔离模式 diff --git "a/docs/\350\256\276\350\256\241\350\257\264\346\230\216.md" "b/docs/\350\256\276\350\256\241\350\257\264\346\230\216.md" index 6d83f56..16dce54 100644 --- "a/docs/\350\256\276\350\256\241\350\257\264\346\230\216.md" +++ "b/docs/\350\256\276\350\256\241\350\257\264\346\230\216.md" @@ -214,11 +214,13 @@ graph LR .task/ -> Git -> 所有人共享同一份 todo.json 现模型 - .task/alice/ -> Alice 本地续作 + .task/alice/ -> Alice 本地续作;syncActiveTasks=true 时 active/todo 可随 Git 同步 .task/bob/ -> Bob 本地续作 .Knowledge/ -> Git -> 团队共享事实 ``` +跨机续作没有回到“仓库共有同一份 todo”:`developerId` 分层仍然保留,同步范围只限进行中任务与 `todo.json`,`completed/` 留在本地;两边还必须解析出同一个 `developerId`。这条能力由 `collaboration.syncActiveTasks` 控制,默认开启,也可关闭为纯本地。 + 这次变化解决的不是文件冲突本身,而是“谁有资格声明这份状态”的问题。任务做到第几步,由执行者自己的 `TASK_ROOT` 负责;能力最终是什么,由代码、正式文档和 `.Knowledge/` 负责。前者可以随会话变化,后者必须经过团队评审。 `developerId` 仍保留在本地路径中,用来覆盖共享开发机、devcontainer 和 Agent 越界读取等场景。它不是远程账号体系,也不承担团队权限管理。 diff --git a/flow2spec.config.json b/flow2spec.config.json index 2494843..0341673 100644 --- a/flow2spec.config.json +++ b/flow2spec.config.json @@ -13,6 +13,7 @@ "locale": "zh-CN", "collaboration": { "enabled": true, - "developerId": "" + "developerId": "", + "syncActiveTasks": true } } diff --git a/package-lock.json b/package-lock.json index bbde640..cee77d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,10 +25,10 @@ }, "packages/cli": { "name": "@double-coding/flow2spec", - "version": "3.6.7", + "version": "3.6.8", "license": "ISC", "dependencies": { - "@double-coding/flow2spec-core": "^3.8.4" + "@double-coding/flow2spec-core": "^3.8.5" }, "bin": { "flow2spec": "cli.js" @@ -39,7 +39,7 @@ }, "packages/core": { "name": "@double-coding/flow2spec-core", - "version": "3.8.4", + "version": "3.8.5", "license": "ISC", "engines": { "node": ">=16" diff --git a/packages/cli/README.md b/packages/cli/README.md index e21954e..b95aeb2 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -61,11 +61,11 @@ Flow2Spec 不是为了增加文档数量。它把项目事实保存在一层小 | 主题分片 | 保存 API、上限、锁、数据规则、业务流程等项目事实。 | `.Knowledge/topics/*.md` | | Agent 入口 | 为选中的 AI 编程客户端安装规则和技能。 | 客户端配置根、`.dsh/`、`AGENTS.md` | | 技能工作流 | 澄清需求、编写方案、实现、修复、同步知识、提交。 | `f2s-*` skills | -| 团队协作 | 每个人的任务现场留在本地,确认后的知识通过结构化 delta 与 topic revision 合入共享仓库。 | `.task//`, `.Knowledge/` | +| 团队协作 | 进行中任务可随代码同步以跨电脑续作,completed 归档留在本地;确认后的知识通过结构化 delta 与 topic revision 合入共享仓库。 | `.task//`, `.Knowledge/` | ## 多人共用一份知识库 -Flow2Spec 按所有权拆分协作状态。checklist、会话上下文和用户代办保存在每名开发者自己的 `TASK_ROOT`,默认不进 Git;已经确认的项目知识统一进入 `.Knowledge/`。 +Flow2Spec 按所有权拆分协作状态。checklist、会话上下文和用户代办保存在每名开发者自己的 `TASK_ROOT`;`collaboration.syncActiveTasks=true`(默认)时进行中任务可随代码提交,`completed/` 归档留在本地;已经确认的项目知识统一进入 `.Knowledge/`。 知识类技能先生成结构化 `kb-delta.json`,不直接改 topic。真正 apply 前,CLI 会比较 delta 的 `baseRevisions` 与磁盘上的 topic revision。修改不同 topic 可以分别合入;两个人同时修改同一 topic 时,后合入的一方需要先拉取最新版本、重读语义,再改写 delta。 diff --git a/packages/cli/cli.js b/packages/cli/cli.js index dbf51cc..366fd9e 100755 --- a/packages/cli/cli.js +++ b/packages/cli/cli.js @@ -371,7 +371,7 @@ init 会: 传 --reset-knowledge 时才会强制用模板覆盖 .Knowledge 中模板承载部分。 4. 在各 agent 配置根写入对应的 rules、skills、入口和 hooks(若该客户端支持);具体落盘方式以客户端适配器为准。 5. 每次 init 将当前 locale 包模板 knowledge/index.md 复制到 .Knowledge/template/index.template.md,供 f2s-kb-upgrade 技能与 .Knowledge/index.md 对照;不自动改写 index.md。(「知识库升级」指 f2s-kb-upgrade 技能,init 本身不是升级命令。) - 6. 维护 .gitignore:仅忽略 .task/completed/、.task/*/completed/ 与 .Knowledge/update-check.json,进行中任务可提交。 + 6. 维护 .gitignore:collaboration.syncActiveTasks=true(默认)时仅忽略 .task/completed/、.task/*/completed/,进行中任务可提交;false 时忽略整个 .task/。同时忽略 .Knowledge/update-check.json。 7. 规则与技能在各 agent 配置根加载;其他模版类文件在 .Knowledge/template/ 等目录。 更多说明见 README.md 或 docs/使用说明.md @@ -994,10 +994,15 @@ if (sub === "init") { ? ` - .Knowledge/template/index.template.md:未复制(${indexSnapshot?.reason || "skip"})` : ` - .Knowledge/template/index.template.md:已从包内 templates/${locale}/knowledge/index.md 复制(与 .Knowledge/index.md 对照见 f2s-kb-upgrade 技能)`; const pc = projectConfig || {}; - const configLine = ` - ${CONFIG_FILENAME}:locale=${pc.locale || locale}, subAgent=${Boolean(pc.subAgent)}, switchAgentVerification=${Boolean(pc.switchAgentVerification)}`; - const gitignoreLine = gitignoreResult?.changed - ? ` - .gitignore:已补充 ${gitignoreResult.added.join(", ")}` - : " - .gitignore:Flow2Spec 本地态忽略项已存在"; + const configLine = ` - ${CONFIG_FILENAME}:locale=${pc.locale || locale}, subAgent=${Boolean(pc.subAgent)}, switchAgentVerification=${Boolean(pc.switchAgentVerification)}, syncActiveTasks=${pc.collaboration?.syncActiveTasks !== false}`; + const gi = gitignoreResult || {}; + const giParts = []; + if (gi.added?.length) giParts.push(`补充 ${gi.added.join(", ")}`); + if (gi.removed?.length) giParts.push(`移除 ${gi.removed.join(", ")}`); + const giMode = gi.mode === "local" ? "任务同步关闭" : "任务同步开启"; + const gitignoreLine = gi.changed + ? ` - .gitignore:${giParts.join(";") || "已更新"}(${giMode})` + : ` - .gitignore:${giMode},忽略项已是最新`; console.log(` ✓ Flow2Spec init 完成 ${knowledgeLine} diff --git a/packages/cli/package.json b/packages/cli/package.json index 5485bd7..cdc72ee 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@double-coding/flow2spec", - "version": "3.6.7", + "version": "3.6.8", "description": "在业务仓库初始化文档驱动、可写回知识库的 AI 协作骨架", "homepage": "https://github.com/double-coding-lab/Flow2Spec#readme", "repository": { @@ -19,7 +19,7 @@ "README.md" ], "dependencies": { - "@double-coding/flow2spec-core": "^3.8.4" + "@double-coding/flow2spec-core": "^3.8.5" }, "publishConfig": { "access": "public", diff --git a/packages/core/lib/codexAgentsAdapter.js b/packages/core/lib/codexAgentsAdapter.js index b217732..6f453b3 100644 --- a/packages/core/lib/codexAgentsAdapter.js +++ b/packages/core/lib/codexAgentsAdapter.js @@ -32,7 +32,8 @@ function renderProjectConfigBlock() { "| `changeTracking.fix` | `false` | `true` 时 `f2s-kb-fix` 步骤 0 必须创建/续作 `.task/active/` 变更追踪任务;`false` 时跳过。 |", "| `changeTracking.implement` | `true` | `true` 时 `f2s-implement-tech-design` 写入任务清单并在满足归档门禁后归档;`false` 时跳过变更追踪部分。 |", "| `collaboration.enabled` | `true` | `true` 时按 developerId 隔离任务根 `.task//`;`false` 时始终 legacy 单根 `.task/`。 |", - "| `collaboration.developerId` | `\"\"` | 非空则作为任务进度目录名;空则尝试 git user.email/name 规范化;仍无则 legacy `.task/`。解析顺序:config → git → legacy。 |", + "| `collaboration.developerId` | `\"\"` | 非空则作为任务进度目录名;空则尝试 git user.email/name 规范化;仍无则 legacy `.task/`。解析顺序:config → git → legacy。跨机续作要求两端解析结果一致(推荐两端使用相同 git user.email)。 |", + "| `collaboration.syncActiveTasks` | `true` | `true` 时仅忽略 `.task/completed/` 与 `.task/*/completed/`,进行中任务可随 Git 同步;`false` 时整个 `.task/` 留在本地。 |", ].join("\n"); } diff --git a/packages/core/lib/doctor.js b/packages/core/lib/doctor.js index f0ed436..7adb884 100644 --- a/packages/core/lib/doctor.js +++ b/packages/core/lib/doctor.js @@ -272,9 +272,9 @@ function runDoctor(cwd = process.cwd(), options = {}) { warnings.length > 0 ? STATUS.warning : STATUS.pass, context.legacy ? `使用 ${context.taskRoot}(${context.enabled ? "legacy" : "协作隔离已关闭"})。` - : `developerId=${context.developerId},TASK_ROOT=${context.taskRoot}。`, + : `developerId=${context.developerId}(source=${context.source}),TASK_ROOT=${context.taskRoot}。`, warnings.length > 0 - ? "在 flow2spec.config.json 配置 collaboration.developerId。" + ? "两端使用相同的 git user.email/name;单人仓库也可在 flow2spec.config.json 配置 collaboration.developerId。" : null, { ...context, warnings }, ), @@ -292,19 +292,34 @@ function runDoctor(cwd = process.cwd(), options = {}) { } } - const taskIgnored = isIgnoredByRootGitignore(cwd, ".task/completed/") && - isIgnoredByRootGitignore(cwd, ".task/*/completed/") && - !isIgnoredByRootGitignore(cwd, ".task/") && - !isIgnoredByRootGitignore(cwd, "/.task/"); + const syncActiveTasks = config?.collaboration?.syncActiveTasks !== false; + const taskIgnored = syncActiveTasks + ? isIgnoredByRootGitignore(cwd, ".task/completed/") && + isIgnoredByRootGitignore(cwd, ".task/*/completed/") && + !isIgnoredByRootGitignore(cwd, ".task/") && + !isIgnoredByRootGitignore(cwd, "/.task/") + : isIgnoredByRootGitignore(cwd, ".task/"); checks.push( taskIgnored - ? makeCheck("task-ignore", "任务目录", STATUS.pass, "已忽略 completed 归档,进行中任务可提交。") + ? makeCheck( + "task-ignore", + "任务目录", + STATUS.pass, + syncActiveTasks + ? "已忽略 completed 归档,进行中任务可提交。" + : "任务同步已关闭,整个 .task/ 保留在本地。", + ) : makeCheck( "task-ignore", "任务目录", STATUS.warning, - "任务忽略规则未对齐:应仅忽略 completed 归档。", - "将整目录 .task/ 规则替换为 .task/completed/ 与 .task/*/completed/,或重新运行 flow2spec init。", + syncActiveTasks + ? "任务忽略规则未对齐:应仅忽略 completed 归档。" + : "任务同步已关闭,但整个 .task/ 未被忽略。", + syncActiveTasks + ? "将整目录 .task/ 规则替换为 .task/completed/ 与 .task/*/completed/,或重新运行 flow2spec init。" + : "在根 .gitignore 中加入 .task/,或重新运行 flow2spec init。", + { syncActiveTasks }, ), ); diff --git a/packages/core/lib/flow2specConfig.js b/packages/core/lib/flow2specConfig.js index bbe8bf0..3407112 100644 --- a/packages/core/lib/flow2specConfig.js +++ b/packages/core/lib/flow2specConfig.js @@ -21,9 +21,11 @@ const DEFAULTS = { }, // 多人协作:进度按 developerId 隔离到 .task//;缺省 enabled // developerId 解析:config → git → legacy 单根 .task/(见 lib/developerId.js) + // syncActiveTasks:true 时进行中任务可随 Git 同步;false 时整个 .task/ 留在本地 collaboration: { enabled: true, developerId: "", + syncActiveTasks: true, }, }; @@ -88,6 +90,13 @@ const CONFIG_FIELDS = [ question: "启用多人协作进度隔离(.task//;关闭则始终用单人 .task/ 根路径)?", }, + { + key: "collaboration.syncActiveTasks", + type: "boolean", + default: true, + question: + "允许进行中任务随 Git 同步以跨电脑续作?(默认 Y;关闭则整个 .task/ 留在本地)", + }, ]; function normalizeBool(value, fallback) { @@ -196,6 +205,10 @@ function loadFlow2specConfig(cwd) { out.collaboration = { enabled: normalizeBool(collab.enabled, DEFAULTS.collaboration.enabled), developerId: idRaw, + syncActiveTasks: normalizeBool( + collab.syncActiveTasks, + DEFAULTS.collaboration.syncActiveTasks, + ), }; } } diff --git a/packages/core/lib/init.js b/packages/core/lib/init.js index daa7fa3..7b8aae1 100644 --- a/packages/core/lib/init.js +++ b/packages/core/lib/init.js @@ -48,21 +48,36 @@ function removeKnowledgeUpdateCheckCache(cwd) { } } -function ensureFlow2specGitignore(cwd) { +function ensureFlow2specGitignore(cwd, options = {}) { + const syncActiveTasks = options.syncActiveTasks !== false; const gitignorePath = path.join(cwd, ".gitignore"); - const required = [".task/completed/", ".task/*/completed/", ".Knowledge/update-check.json"]; const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8") : ""; - // Migrate the blanket ignore written by earlier init versions so active tasks - // and their index can travel with the repository between computers. - const retained = existing.split(/\r?\n/).filter((line) => - ![".task/", ".task", "/.task/", "/.task"].includes(line.trim()), - ).join(existing.includes("\r\n") ? "\r\n" : "\n"); + const eol = existing.includes("\r\n") ? "\r\n" : "\n"; + // true:迁移旧的整目录规则,只忽略 completed 归档;false:恢复整目录忽略。 + const remove = syncActiveTasks + ? [".task/", ".task", "/.task/", "/.task"] + : [".task/completed/", ".task/*/completed/"]; + const required = syncActiveTasks + ? [".task/completed/", ".task/*/completed/", ".Knowledge/update-check.json"] + : [".task/", ".Knowledge/update-check.json"]; + const originalLines = existing.split(/\r?\n/); + const retained = originalLines + .filter((line) => !remove.includes(line.trim())) + .join(eol); + const removed = [ + ...new Set( + originalLines + .map((line) => line.trim()) + .filter((line) => remove.includes(line)), + ), + ]; const lines = retained.split(/\r?\n/).map((line) => line.trim()); const missing = required.filter((item) => !lines.includes(item)); - if (missing.length === 0 && retained === existing) { - return { path: gitignorePath, changed: false, added: [] }; + const mode = syncActiveTasks ? "sync" : "local"; + if (missing.length === 0 && removed.length === 0) { + return { path: gitignorePath, changed: false, added: [], removed: [], mode }; } const additions = []; if (missing.length > 0 && !lines.includes("# Flow2Spec local state")) { @@ -72,10 +87,10 @@ function ensureFlow2specGitignore(cwd) { const prefix = retained && !retained.endsWith("\n") ? `${retained}\n` : retained; const separator = prefix && !prefix.endsWith("\n\n") ? "\n" : ""; const next = additions.length > 0 - ? `${prefix}${separator}${additions.join("\n")}\n` + ? `${prefix}${separator}${additions.join(eol)}\n` : retained; fs.writeFileSync(gitignorePath, next, "utf8"); - return { path: gitignorePath, changed: true, added: missing }; + return { path: gitignorePath, changed: true, added: missing, removed, mode }; } function ensureAgentDirs(cwd, agentId) { @@ -1265,11 +1280,14 @@ async function run(cwd, agentIds, options = {}) { ensureKnowledgeDirs(cwd); removeKnowledgeUpdateCheckCache(cwd); - const gitignoreResult = ensureFlow2specGitignore(cwd); ensureFlow2specProjectConfig(cwd, templatesDir, { overwrite: false, values: Object.keys(effectiveConfigValues).length ? effectiveConfigValues : undefined, }); + const projectConfig = loadFlow2specConfig(cwd); + const gitignoreResult = ensureFlow2specGitignore(cwd, { + syncActiveTasks: projectConfig.collaboration.syncActiveTasks, + }); const knowledgeResult = copyKnowledgeTemplates(cwd, templatesDir, { overwrite: overwriteKnowledge, }); @@ -1281,8 +1299,6 @@ async function run(cwd, agentIds, options = {}) { const indexSnapshot = copyKnowledgeIndexTemplateSnapshot(cwd, templatesDir); - const projectConfig = loadFlow2specConfig(cwd); - const claudeHooksResult = {}; for (const id of ids) { // 虚拟目标(root: null,如 plugin 插件模式):能力由客户端插件提供,不写配置根。 diff --git a/packages/core/package.json b/packages/core/package.json index 06698ff..b37c6e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@double-coding/flow2spec-core", - "version": "3.8.4", - "templateVersion": "3.8.2", + "version": "3.8.5", + "templateVersion": "3.8.3", "description": "Flow2Spec Core APIs, knowledge engine, project initialization and shared resources", "homepage": "https://github.com/double-coding-lab/Flow2Spec#readme", "repository": { diff --git a/packages/core/templates/en-US/flow2spec.config.json b/packages/core/templates/en-US/flow2spec.config.json index 92e2810..2513034 100644 --- a/packages/core/templates/en-US/flow2spec.config.json +++ b/packages/core/templates/en-US/flow2spec.config.json @@ -13,6 +13,7 @@ }, "collaboration": { "enabled": true, - "developerId": "" + "developerId": "", + "syncActiveTasks": true } } diff --git a/packages/core/templates/en-US/knowledge/manifest-routing.json b/packages/core/templates/en-US/knowledge/manifest-routing.json index 87ba060..c561ecf 100644 --- a/packages/core/templates/en-US/knowledge/manifest-routing.json +++ b/packages/core/templates/en-US/knowledge/manifest-routing.json @@ -1,5 +1,5 @@ { - "version": "3.8.2", + "version": "3.8.3", "projectRev": 3, "knowledgeRoot": ".Knowledge", "matcherKey": "matcherId", diff --git a/packages/core/templates/en-US/rules/f2s-task.md b/packages/core/templates/en-US/rules/f2s-task.md index 548ada2..1d67ebb 100644 --- a/packages/core/templates/en-US/rules/f2s-task.md +++ b/packages/core/templates/en-US/rules/f2s-task.md @@ -82,6 +82,22 @@ TASK_ROOT/ <- `.task` or `.task/` **Migration from single-root**: if root `.task/active/` still exists while `TASK_ROOT=.task/`, move only after user confirmation. +## Git and cross-computer continuation + +`collaboration.syncActiveTasks` controls whether in-progress tasks travel with Git (default `true`): + +- `true`: `.gitignore` ignores only `.task/completed/` and `.task/*/completed/`; `active/`, `todo.json`, and `kb-delta.json` can be committed with the code. +- `false`: the whole `.task/` stays local; `flow2spec init` writes the blanket ignore rule. + +Prerequisites for continuing on another computer: + +1. Both machines must resolve the **same `developerId`** (preferably set the same `git config user.email`; `flow2spec doctor` shows the current source and `TASK_ROOT`). +2. Commit `TASK_ROOT/active/**`, `todo.json`, and the corresponding code in the same commit/branch. A checklist without the pushed code cannot be resumed. +3. **Scan for sensitive data before committing**: `user-todos.md` / `context.md` must not contain passwords, tokens, private keys, database connection strings, or internal addresses. Describe *what* the user must do; keep the values local. +4. Archiving moves `active/` into `completed/`. If the task was previously committed, commit the deletion Git reports so another machine does not keep seeing a stale active task. + +When `syncActiveTasks=false`, do not commit any of the above; the task state stays local. + ## todo.json Structure ```json @@ -205,6 +221,7 @@ At the start of a new session, resolve **`TASK_ROOT` first**; if `TASK_ROOT/todo 2. **During execution**: each time a new category of user todo appears, append it **in that turn** (recommended: second-level heading by date `## YYYY-MM-DD`, followed by `- [ ]` checklist items or step numbers). 3. **Division from `task.md`**: `task.md` tracks Agent-side step checkboxes; `user-todos.md` tracks user-side pending items. **Do not** write long "user-only" operation instructions only in `task.md` as a substitute for this file. 4. **Continuation**: when loading a task, `Read` this file and show the user any `- [ ]` items that remain unchecked. +5. **Never write credentials**: describe what the user must provide or execute; do not persist passwords, tokens, private keys, database connection strings, or internal addresses. Task files can travel through Git. **Example structure**: diff --git a/packages/core/templates/en-US/skills/f2s-git-commit/SKILL.md b/packages/core/templates/en-US/skills/f2s-git-commit/SKILL.md index de04c48..a0524a3 100644 --- a/packages/core/templates/en-US/skills/f2s-git-commit/SKILL.md +++ b/packages/core/templates/en-US/skills/f2s-git-commit/SKILL.md @@ -49,6 +49,12 @@ Scan all changed file contents. If any file contains conflict markers `<<<<<<<`, Please resolve the conflict before committing. ``` +**Task-file sync and security check (only when pending files include `.task/**`)**: + +1. Read `collaboration.syncActiveTasks` from `flow2spec.config.json` (default `true`). If it is `false` but `.task/**` changes still appear, the root `.gitignore` is not taking effect; stop and ask the user to fix the ignore rules. Do not commit task files. +2. Scan the `.task/**` files to be committed (especially `user-todos.md` / `context.md` / `todo.json` / `kb-delta.json`) for sensitive data: passwords, tokens, secrets, API keys, private keys, database connection strings, and internal addresses. On a hit, stop, list the file and line, and ask the user to remove it or replace it with a placeholder. Do not commit. +3. When the check passes, commit `.task/**` together with the code; also commit the deletion diff produced by archiving a task. Mention which task files were synced in the closing note. + ### Step 2: Knowledge-Base Coverage Check (Required by Default; Skipped for Quick Commit) If in **quick commit mode**, skip this step and mention in the Step 5 closing note that "the knowledge-base coverage check was skipped according to quick commit mode." @@ -65,6 +71,7 @@ If **every** pending file collected in Step 1 matches one of these patterns, ski - `docs/**` / `docs/en/**` - `README*.md` / `LICENSE` / `CHANGELOG*` - `.claude/**` / `.cursor/**` / `.codex/**` (agent config roots; distributed by `flow2spec init` and unrelated to business capability coverage) +- `.task/**` (personal task working state; whether it travels with the code is controlled by `collaboration.syncActiveTasks`) - `presentations/**` / `assets/**` / other pure static resources **Any** file falling under `src/` / `lib/` / `cli.js` / `templates/` / business code directories disables this shortcut; continue to the coverage check. @@ -222,6 +229,9 @@ git commit -m "" [If skip rule B matched: recent sync within 30 min] 🔄 Skipped coverage check because ran within the last 30 min (.Knowledge/.last-sync.json). + +[If this commit includes .task/ files] +📌 Synced in-progress task file(s). On another computer, pull and resume with the same developerId resolution; never write credentials into task files. ``` ## Constraints @@ -235,6 +245,7 @@ git commit -m "" - **`git commit`**: a separate user reply of "confirm" is **not required**; however, it is **forbidden** to commit without showing the proposed first line in the same reply first. - The commit-message **first line** must follow the **emoji + type** format in Step 3 (keep a user-provided compliant line as-is). - If merge-conflict markers exist, stop and do not continue. +- Whether `.task/**` enters the commit is controlled by `collaboration.syncActiveTasks`; when it is `false`, do not commit `.task/`. The sensitive-data scan is mandatory before committing task files. ## Completion Self-Check @@ -247,5 +258,6 @@ git commit -m "" 7. Does the commit-message **first line** match ` [(scope)]: `, with emoji and type consistent with the table? Exceptions such as merge revert must be explained when shown. 8. If pre-commit failed, was the hook bypassed? Must be no. 9. If Step 2 chose B, does the closing note include an uncovered-knowledge reminder? +10. If pending files include `.task/**`, was `syncActiveTasks` checked, was the sensitive-data scan completed, and does the closing note list the synced task files? 10. If Step 2 chose A, does the flow continue after the user records knowledge or triggers again (**without** requiring a separate confirmation just to continue commit)? 11. If this flow ever needed `git pull`: was explicit confirmation for **pull** obtained before running it? Must be yes; if not involved, mark N/A. diff --git a/packages/core/templates/zh-CN/flow2spec.config.json b/packages/core/templates/zh-CN/flow2spec.config.json index e8c0197..fc657ba 100644 --- a/packages/core/templates/zh-CN/flow2spec.config.json +++ b/packages/core/templates/zh-CN/flow2spec.config.json @@ -13,6 +13,7 @@ }, "collaboration": { "enabled": true, - "developerId": "" + "developerId": "", + "syncActiveTasks": true } } diff --git a/packages/core/templates/zh-CN/knowledge/manifest-routing.json b/packages/core/templates/zh-CN/knowledge/manifest-routing.json index 038f118..3ac1344 100644 --- a/packages/core/templates/zh-CN/knowledge/manifest-routing.json +++ b/packages/core/templates/zh-CN/knowledge/manifest-routing.json @@ -1,5 +1,5 @@ { - "version": "3.8.2", + "version": "3.8.3", "projectRev": 3, "knowledgeRoot": ".Knowledge", "matcherKey": "matcherId", diff --git a/packages/core/templates/zh-CN/rules/f2s-task.md b/packages/core/templates/zh-CN/rules/f2s-task.md index 6981ece..1e84760 100644 --- a/packages/core/templates/zh-CN/rules/f2s-task.md +++ b/packages/core/templates/zh-CN/rules/f2s-task.md @@ -82,6 +82,22 @@ TASK_ROOT/ ← `.task` 或 `.task/` **从单人布局迁移**:若磁盘仍有根级 `.task/active/` 与 `.task/todo.json`,而当前已解析出非 legacy 的 `TASK_ROOT=.task/`,可在用户确认后将旧 `active/*` 与条目迁入新根(一次性);未确认前 **不要** 自动挪动他人可能共用的根目录。 +## Git 与跨电脑续作 + +`collaboration.syncActiveTasks` 控制进行中任务是否随 Git 同步(默认 `true`): + +- `true`:`.gitignore` 仅忽略 `.task/completed/` 与 `.task/*/completed/`;`active/`、`todo.json`、`kb-delta.json` 可与代码一起提交。 +- `false`:整个 `.task/` 留在本地;`flow2spec init` 会写回整目录忽略规则。 + +跨电脑续作的前提: + +1. 两端解析出**相同 `developerId`**(推荐两端设置相同 `git config user.email`;`flow2spec doctor` 会显示当前来源与 `TASK_ROOT`)。 +2. 提交时把 `TASK_ROOT/active/**`、`todo.json` 与对应代码放在同一组提交/分支;只提交 checklist 而代码未推送,另一台机器无法继续实现。 +3. **提交前必须检查敏感信息**:`user-todos.md` / `context.md` 不得写入密码、token、私钥、数据库连接串、内网地址;只写“需要做什么”,值由用户在本地提供。 +4. 归档时 `active/` 会移入 `completed/`;若该任务此前已提交,必须把 Git 显示的删除一并提交,避免另一台机器继续看到过期任务。 + +`syncActiveTasks=false` 时不要执行上述提交;任务现场只在本机续作。 + ## todo.json 结构 ```json @@ -207,6 +223,7 @@ TASK_ROOT/ ← `.task` 或 `.task/` 2. **执行中**:每出现一类新的用户代办,**当次**追加(推荐按日期分二级标题 `## YYYY-MM-DD`,下列 `- [ ]` 可勾选项或步骤编号)。 3. **与 `task.md` 分工**:`task.md` 管 Agent 侧步骤 checkbox;`user-todos.md` 管用户侧待办;**勿**把「仅用户可执行」的长操作说明只写在 `task.md` 步骤正文代替本文件。 4. **续作**:加载任务时 `Read` 本文件,向用户展示仍未勾选的 `- [ ]` 项(若有)。 +5. **禁止写入凭据**:密码、token、私钥、数据库连接串、内网地址等只写“需要用户提供/执行”,不要把值落盘;任务文件可能随 Git 提交。 **示例结构**: diff --git a/packages/core/templates/zh-CN/skills/f2s-git-commit/SKILL.md b/packages/core/templates/zh-CN/skills/f2s-git-commit/SKILL.md index 286d389..ef503c2 100644 --- a/packages/core/templates/zh-CN/skills/f2s-git-commit/SKILL.md +++ b/packages/core/templates/zh-CN/skills/f2s-git-commit/SKILL.md @@ -49,6 +49,12 @@ git diff HEAD 请先解决冲突后再提交。 ``` +**任务文件同步与安全检查(仅当 pending 含 `.task/**`)**: + +1. 读取 `flow2spec.config.json` 的 `collaboration.syncActiveTasks`(缺省 `true`)。若为 `false` 却仍出现 `.task/**` 变更,说明根 `.gitignore` 未生效;先停止提交并提示用户修正忽略规则,不要提交任务文件。 +2. 对将提交的 `.task/**` 文件(重点 `user-todos.md` / `context.md` / `todo.json` / `kb-delta.json`)做敏感信息扫描:密码、token、secret、API key、私钥、数据库连接串、内网地址。命中即停止,列出文件与行号,要求用户移除或改为占位;不得直接提交。 +3. 若检查通过,`.task/**` 与代码一起提交;归档任务产生的删除 diff 也要一并提交。收尾提示中说明同步了哪些任务文件。 + ### 步骤 2:知识库覆盖检查(默认必须;三种情况可跳过) 若处于**快捷提交模式**,本步骤直接跳过,并在步骤 5 收尾提示中说明“已按快捷提交跳过知识库覆盖检查”。 @@ -65,6 +71,7 @@ git diff HEAD - `docs/**` / `docs/en/**` - `README*.md` / `LICENSE` / `CHANGELOG*` - `.claude/**` / `.cursor/**` / `.codex/**`(agent 配置根,由 flow2spec init 分发,与业务能力覆盖无关) +- `.task/**`(个人任务过程状态;是否随代码同步由 `collaboration.syncActiveTasks` 决定) - `presentations/**` / `assets/**` / 其他纯静态资源 **任一**文件落在 `src/` / `lib/` / `cli.js` / `templates/` / 业务代码目录时,本捷径**不生效**,继续走覆盖检查。 @@ -222,6 +229,9 @@ git commit -m "<步骤 3 定稿的完整提交信息>" [若命中跳过判定 B:近 30 分钟内已同步] 🔄 近 30 分钟内已跑过 ,已跳过覆盖检查(.Knowledge/.last-sync.json)。 + +[若本次提交包含 .task/ 文件] +📌 已同步 个进行中任务文件。另一台电脑拉取后,用相同 developerId 解析结果续作;凭据不得写入任务文件。 ``` ## 约束 @@ -235,6 +245,7 @@ git commit -m "<步骤 3 定稿的完整提交信息>" - **`git commit`**:**不要求**用户单独回复「确认」;但**禁止完全不展示**拟提交首行就执行 commit(须在当条回复中可见首行后再执行)。 - 提交信息**首行**须符合步骤 3 的 **emoji + type** 格式(用户已合规给出时可保留)。 - 存在 merge conflict 标记时必须终止,不得继续。 +- `.task/**` 是否进入提交由 `collaboration.syncActiveTasks` 决定;该开关为 `false` 时禁止提交 `.task/`。提交前必须完成敏感信息扫描。 ## 完成后自检 @@ -247,5 +258,6 @@ git commit -m "<步骤 3 定稿的完整提交信息>" 7. 提交信息**首行**是否为 ` [(scope)]: <简述>` 且 emoji 与 type 与上表一致(合并 revert 等例外须在展示中说明)。 8. 若 pre-commit 失败,是否跳过了 hook(必须为否)。 9. 若步骤 2 选 B,收尾提示是否包含未补录提醒。 +10. 若 pending 含 `.task/**`,是否检查了 `syncActiveTasks`、完成敏感信息扫描,并在收尾提示中说明同步的任务文件? 10. 若步骤 2 选 A,是否在用户补录或再次触发后继续流程(**不要求**为继续 commit 单独要确认)。 11. 若本流程中曾需要 `git pull`:是否在执行前取得用户对 **pull** 的明确确认(必须为是);未涉及 pull 则标 N/A。 diff --git "a/presentations/flow2spec-intro-public/\346\274\224\350\256\262\347\250\277.md" "b/presentations/flow2spec-intro-public/\346\274\224\350\256\262\347\250\277.md" index 4db2a84..79e6495 100644 --- "a/presentations/flow2spec-intro-public/\346\274\224\350\256\262\347\250\277.md" +++ "b/presentations/flow2spec-intro-public/\346\274\224\350\256\262\347\250\277.md" @@ -222,7 +222,7 @@ user-todos.md 管另一类事情:数据库变更、环境配置、人工确认 回到主仓 A 的数据:三个月、27 位作者、2786 次 commit、43 个归档任务。在这样的协作规模下,所有人共用一份任务进度显然不合适。 -个人现场放在 .task//,默认不进 Git;确认后的项目知识放在 .Knowledge/,随代码通过 Git 和 PR 共享。developerId 可以显式配置,也可以从 Git 身份解析。团队观察进展主要看 PR、提交和知识变更,而不是把个人任务目录当成团队看板。 +个人现场放在 .task//:默认只把 completed 归档留在本地,进行中任务可按 collaboration.syncActiveTasks 随代码同步,便于同一开发者换电脑续作;确认后的项目知识放在 .Knowledge/,随代码通过 Git 和 PR 共享。developerId 可以显式配置,也可以从 Git 身份解析。团队观察进展主要看 PR、提交和知识变更,而不是把个人任务目录当成团队看板。 共享知识也不能悄悄覆盖。kb-delta.json 记录变更意图,topic revision 用来检查这次修改基于哪个版本。版本已经变化,就先停下来重读最新事实、调整变更。这个 revision 冲突,与下一页的 Git 文本冲突,是两种不同情况。 diff --git a/scripts/test-core-api.js b/scripts/test-core-api.js index 6c422cc..1c4c9b4 100644 --- a/scripts/test-core-api.js +++ b/scripts/test-core-api.js @@ -44,6 +44,10 @@ async function main() { await api.project.init({ mode: "native-host", integrations: ["dsh"], locale: "zh-CN" }); assert.ok(fs.existsSync(path.join(tempDir, ".Knowledge"))); assert.ok(fs.existsSync(path.join(tempDir, "flow2spec.config.json"))); + const projectConfig = JSON.parse( + fs.readFileSync(path.join(tempDir, "flow2spec.config.json"), "utf8"), + ); + assert.strictEqual(projectConfig.collaboration.syncActiveTasks, true); assert.ok(!fs.existsSync(path.join(tempDir, ".dsh"))); const report = api.knowledge.check({ strict: true }); diff --git a/scripts/test-doctor.js b/scripts/test-doctor.js index 56ddb84..e0db068 100644 --- a/scripts/test-doctor.js +++ b/scripts/test-doctor.js @@ -18,7 +18,7 @@ function write(file, content = "") { function createHealthyProject(root) { write( path.join(root, "flow2spec.config.json"), - `${JSON.stringify({ collaboration: { enabled: true, developerId: "alice" } })}\n`, + `${JSON.stringify({ collaboration: { enabled: true, developerId: "alice", syncActiveTasks: true } })}\n`, ); write(path.join(root, "AGENTS.md"), "# Project\n"); write(path.join(root, ".Knowledge", "manifest-routing.json"), "{}\n"); @@ -40,6 +40,7 @@ assert.strictEqual(satisfiesNodeEngine("v15.9.0", ">=16"), false); assert.strictEqual(satisfiesNodeEngine("v20.11.1", ">=16"), true); const healthyRoot = fs.mkdtempSync(path.join(os.tmpdir(), "flow2spec-doctor-ok-")); +const localRoot = fs.mkdtempSync(path.join(os.tmpdir(), "flow2spec-doctor-local-")); const warningRoot = fs.mkdtempSync(path.join(os.tmpdir(), "flow2spec-doctor-warn-")); const errorRoot = fs.mkdtempSync(path.join(os.tmpdir(), "flow2spec-doctor-error-")); @@ -67,6 +68,29 @@ try { assert.match(taskCheck.repair, /completed/); } + createHealthyProject(localRoot); + write( + path.join(localRoot, "flow2spec.config.json"), + `${JSON.stringify({ collaboration: { enabled: true, developerId: "alice", syncActiveTasks: false } })}\n`, + ); + write(path.join(localRoot, ".gitignore"), "node_modules/\n.task/\n"); + const local = runDoctor(localRoot, { + nodeVersion: "v20.11.1", + knowledgeCheck: passKnowledge, + }); + const localTaskCheck = local.checks.find((check) => check.id === "task-ignore"); + assert.strictEqual(localTaskCheck.status, "pass"); + assert.match(localTaskCheck.message, /任务同步已关闭/); + write(path.join(localRoot, ".gitignore"), "node_modules/\n.task/completed/\n.task/*/completed/\n"); + const localMisconfigured = runDoctor(localRoot, { + nodeVersion: "v20.11.1", + knowledgeCheck: passKnowledge, + }); + assert.strictEqual( + localMisconfigured.checks.find((check) => check.id === "task-ignore").status, + "warning", + ); + write( path.join(warningRoot, "flow2spec.config.json"), `${JSON.stringify({ collaboration: { enabled: true, developerId: "" } })}\n`, @@ -114,6 +138,7 @@ try { console.log("doctor tests passed"); } finally { fs.rmSync(healthyRoot, { recursive: true, force: true }); + fs.rmSync(localRoot, { recursive: true, force: true }); fs.rmSync(warningRoot, { recursive: true, force: true }); fs.rmSync(errorRoot, { recursive: true, force: true }); } diff --git a/scripts/test-init-gitignore.js b/scripts/test-init-gitignore.js index 882459b..51311e3 100644 --- a/scripts/test-init-gitignore.js +++ b/scripts/test-init-gitignore.js @@ -19,6 +19,7 @@ const corePkg = require("../packages/core/package.json"); }, }); assert.strictEqual(first.gitignoreResult.changed, true); + assert.strictEqual(first.gitignoreResult.mode, "sync"); assert.deepStrictEqual(first.gitignoreResult.added, [ ".task/completed/", ".task/*/completed/", @@ -71,8 +72,8 @@ const corePkg = require("../packages/core/package.json"); [".task/alice/completed/example/task.md", true], [".Knowledge/update-check.json", true], ]; - const checkIgnores = () => { - for (const [file, ignored] of taskPaths) { + const checkIgnores = (paths = taskPaths) => { + for (const [file, ignored] of paths) { const result = spawnSync("git", ["check-ignore", "--no-index", "-q", file], { cwd: tmpRoot }); assert.strictEqual(result.status, ignored ? 0 : 1, file); } @@ -92,6 +93,34 @@ const corePkg = require("../packages/core/package.json"); } } + // Switch to local-only mode: the whole .task/ stays ignored. + fs.writeFileSync(gitignorePath, afterFirst, "utf8"); + const local = await runInit(tmpRoot, ["cursor"], { + configValues: { locale: "zh-CN", "collaboration.syncActiveTasks": false }, + }); + assert.strictEqual(local.gitignoreResult.changed, true); + assert.strictEqual(local.gitignoreResult.mode, "local"); + assert(local.gitignoreResult.added.includes(".task/")); + assert(local.gitignoreResult.removed.includes(".task/completed/")); + assert(local.gitignoreResult.removed.includes(".task/*/completed/")); + const afterLocal = fs.readFileSync(gitignorePath, "utf8"); + assert(afterLocal.includes(".task/")); + assert(!afterLocal.includes(".task/completed/")); + const localPaths = taskPaths.map(([file]) => [file, true]); + checkIgnores(localPaths); + const stableLocal = await runInit(tmpRoot, ["cursor"], { + configValues: { locale: "zh-CN", "collaboration.syncActiveTasks": false }, + }); + assert.strictEqual(stableLocal.gitignoreResult.changed, false); + + // Switch back to sync mode: narrow rules return, blanket rule is removed. + const backToSync = await runInit(tmpRoot, ["cursor"], { + configValues: { locale: "zh-CN", "collaboration.syncActiveTasks": true }, + }); + assert.strictEqual(backToSync.gitignoreResult.mode, "sync"); + assert(backToSync.gitignoreResult.removed.includes(".task/")); + checkIgnores(); + console.log("init gitignore tests passed"); } finally { fs.rmSync(tmpRoot, { recursive: true, force: true }); diff --git a/scripts/test-package-install.js b/scripts/test-package-install.js index 41dd61d..c2aabfb 100644 --- a/scripts/test-package-install.js +++ b/scripts/test-package-install.js @@ -25,11 +25,12 @@ const tarballs = fs.readdirSync(tempDir).filter((file) => file.endsWith(".tgz")) assert.strictEqual(tarballs.length, 2, "expected Core and CLI tarballs"); const cliTarball = tarballs.find((file) => /^double-coding-flow2spec-\d/.test(file)); assert.ok(cliTarball, "expected CLI tarball"); -const packedReadme = run("tar", [ - "-xOf", - path.join(tempDir, cliTarball), - "package/README.md", -]); +const tarArgs = ["-xOf", path.join(tempDir, cliTarball), "package/README.md"]; +if (process.platform === "win32") { + // GNU tar interprets "D:" in an absolute path as a remote host without this. + tarArgs.unshift("--force-local"); +} +const packedReadme = run("tar", tarArgs); assert.strictEqual( packedReadme, fs.readFileSync(path.resolve(__dirname, "..", "README.md"), "utf8"),