diff --git a/docs/technical/README.md b/docs/technical/README.md index a37d97d2..b768c952 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -9,6 +9,7 @@ - [PROMPT_DIRECTORY_MANAGEMENT_2026-04-19.md](./PROMPT_DIRECTORY_MANAGEMENT_2026-04-19.md):后端提示词收口到 `server-node/src/prompts/` 的目录方案、兼容策略与后续新增规则。 - [SPACETIME_DEV_URI_HOTFIX_2026-04-20.md](./SPACETIME_DEV_URI_HOTFIX_2026-04-20.md):修复开发默认配置把 Spacetime 连接误指向 Vite `3000` 端口的问题。 - [SPACETIME_AUTH_TOKEN_FALLBACK_HOTFIX_2026-04-20.md](./SPACETIME_AUTH_TOKEN_FALLBACK_HOTFIX_2026-04-20.md):本地 token 失效时自动降级匿名连接,并提示“登录已过期”的热修记录。 +- [TASK_AUTO_COMMIT_WORKFLOW_2026-04-20.md](./TASK_AUTO_COMMIT_WORKFLOW_2026-04-20.md):任务完成后按文件边界自动提交的脚本与协作约定。 - [NODE_DEV_STARTUP_HOTFIX_2026-04-20.md](./NODE_DEV_STARTUP_HOTFIX_2026-04-20.md):`npm run dev` 启动失败的热修记录、根因与验证结果。 - [NODE_SERVER_KNOWLEDGE_GRAPH_2026-04-08.md](./NODE_SERVER_KNOWLEDGE_GRAPH_2026-04-08.md):当前 Node 运行时后端的技术栈、入口、鉴权、存储与接口知识图谱。 - [EXPRESS_BACKEND_INTEGRATION_FREEZE_2026-04-09.md](./EXPRESS_BACKEND_INTEGRATION_FREEZE_2026-04-09.md):Express 后端当前 contract 冻结版本、热点文件编辑规则与集成窗口清单。 diff --git a/docs/technical/TASK_AUTO_COMMIT_WORKFLOW_2026-04-20.md b/docs/technical/TASK_AUTO_COMMIT_WORKFLOW_2026-04-20.md new file mode 100644 index 00000000..76a179c6 --- /dev/null +++ b/docs/technical/TASK_AUTO_COMMIT_WORKFLOW_2026-04-20.md @@ -0,0 +1,71 @@ +# 任务完成自动提交工作流(2026-04-20) + +## 1. 目标 + +为避免“任务已完成但改动长期停留在工作区”与“提交时把无关改动一并打包”的问题,仓库补充一条统一工作流: + +1. 当任务明确完成时,立即执行一次提交。 +2. 只提交当前任务相关文件。 +3. 提交信息使用中文简洁摘要。 +4. 不依赖 `git hook` 或后台自动监听。 + +## 2. 实现方式 + +新增脚本: + +- `scripts/commit-task.mjs` + +职责: + +1. 接收提交信息 `-m/--message` +2. 接收本次任务要提交的文件列表 +3. 先检查这些文件是否确实存在未提交改动 +4. 只暂存传入文件 +5. 只在存在暂存内容时执行提交 + +## 3. 使用方式 + +命令格式: + +```bash +node scripts/commit-task.mjs -m "中文提交摘要" +``` + +示例: + +```bash +node scripts/commit-task.mjs \ + -m "补充任务完成后自动提交协作约定" \ + docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md \ + docs/experience/README.md +``` + +## 4. 行为约束 + +1. 不传 `-m` 会直接失败。 +2. 不传文件列表会直接失败。 +3. 指定文件没有改动时不会创建空提交。 +4. 脚本不会自动把未指定文件加入提交。 +5. 脚本不做 `amend`,不改写历史。 + +## 5. 适用范围 + +适用于: + +1. 单任务完成后的常规提交 +2. 工作区存在其他无关改动时的定向提交 +3. 需要明确控制提交边界的日常开发 + +不适用于: + +1. 需要 squash 或改写历史的场景 +2. 需要交互式挑选 hunk 的场景 +3. 无法清晰界定当前任务文件边界的场景 + +## 6. 当前协作约定 + +默认规则: + +1. 用户明确表示任务完成,或直接要求提交时,优先使用这条工作流。 +2. 若当前任务文件边界清晰,则直接提交。 +3. 若边界不清晰,则先停下并说明风险,不强行提交。 diff --git a/package.json b/package.json index f702172b..5f8d90be 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build:raw": "node scripts/vite-cli.mjs build", "preview": "node scripts/vite-cli.mjs preview", "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"", + "commit:task": "node scripts/commit-task.mjs", "check:encoding": "node scripts/check-encoding.mjs", "lint:eslint": "eslint . --ext .ts,.tsx,.js,.mjs,.cjs --max-warnings 0", "lint:guardrails": "npm run lint:eslint", diff --git a/scripts/commit-task.mjs b/scripts/commit-task.mjs new file mode 100644 index 00000000..c3d47881 --- /dev/null +++ b/scripts/commit-task.mjs @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; + +function fail(message) { + console.error(`[commit-task] ${message}`); + process.exit(1); +} + +function runGit(args, options = {}) { + const result = spawnSync('git', args, { + cwd: '/home/Genarrative', + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'], + ...options, + }); + + if (result.status !== 0) { + const stderr = result.stderr?.trim(); + fail(stderr || `git ${args.join(' ')} 执行失败`); + } + + return result.stdout?.trim() || ''; +} + +const args = process.argv.slice(2); +const messageIndex = args.findIndex((arg) => arg === '-m' || arg === '--message'); + +if (messageIndex === -1) { + fail('缺少提交信息,请使用 -m "中文摘要"'); +} + +const message = args[messageIndex + 1]?.trim() || ''; +if (!message) { + fail('提交信息不能为空'); +} + +const fileArgs = args.filter((_, index) => index !== messageIndex && index !== messageIndex + 1); +if (fileArgs.length === 0) { + fail('至少传入一个需要提交的文件路径'); +} + +const statusOutput = runGit(['status', '--short', '--', ...fileArgs]); +if (!statusOutput) { + fail('指定文件没有可提交改动'); +} + +runGit(['add', '--', ...fileArgs]); + +const stagedOutput = runGit(['diff', '--cached', '--name-only', '--', ...fileArgs]); +if (!stagedOutput) { + fail('暂存后没有检测到可提交内容'); +} + +runGit(['commit', '-m', message], { stdio: 'inherit' }); +