diff --git a/agents/gsd-code-fixer.md b/agents/gsd-code-fixer.md index d856748e9c..7d1ac154ed 100644 --- a/agents/gsd-code-fixer.md +++ b/agents/gsd-code-fixer.md @@ -345,6 +345,7 @@ Use `gsd-sdk query commit` with conventional format (message first, then every s ```bash gsd-sdk query commit \ "fix({padded_phase}): {finding_id} {short_description}" \ + --files \ {all_modified_files} ``` @@ -354,7 +355,7 @@ Examples: **Multiple files:** List ALL modified files after the message (space-separated): ```bash -gsd-sdk query commit "fix(02): CR-01 ..." \ +gsd-sdk query commit "fix(02): CR-01 ..." --files \ src/api/auth.ts src/types/user.ts tests/auth.test.ts ``` diff --git a/agents/gsd-debugger.md b/agents/gsd-debugger.md index 185436fd69..0199d7d895 100644 --- a/agents/gsd-debugger.md +++ b/agents/gsd-debugger.md @@ -1168,7 +1168,7 @@ Root cause: {root_cause}" Then commit planning docs via CLI (respects `commit_docs` config automatically): ```bash -gsd-sdk query commit "docs: resolve debug {slug}" .planning/debug/resolved/{slug}.md +gsd-sdk query commit "docs: resolve debug {slug}" --files .planning/debug/resolved/{slug}.md ``` **Append to knowledge base:** @@ -1199,7 +1199,7 @@ Then append the entry: Commit the knowledge base update alongside the resolved session: ```bash -gsd-sdk query commit "docs: update debug knowledge base with {slug}" .planning/debug/knowledge-base.md +gsd-sdk query commit "docs: update debug knowledge base with {slug}" --files .planning/debug/knowledge-base.md ``` Report completion and offer next steps. diff --git a/agents/gsd-executor.md b/agents/gsd-executor.md index dd8d60ef90..71d258f3d3 100644 --- a/agents/gsd-executor.md +++ b/agents/gsd-executor.md @@ -563,7 +563,7 @@ gsd-sdk query state.add-blocker "Blocker description" ```bash -gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" \ +gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files \ .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md ``` diff --git a/agents/gsd-phase-researcher.md b/agents/gsd-phase-researcher.md index 83baff8750..6f7df87c44 100644 --- a/agents/gsd-phase-researcher.md +++ b/agents/gsd-phase-researcher.md @@ -755,7 +755,7 @@ Write to: `$PHASE_DIR/$PADDED_PHASE-RESEARCH.md` ## Step 7: Commit Research (optional) ```bash -gsd-sdk query commit "docs($PHASE): research phase domain" "$PHASE_DIR/$PADDED_PHASE-RESEARCH.md" +gsd-sdk query commit "docs($PHASE): research phase domain" --files "$PHASE_DIR/$PADDED_PHASE-RESEARCH.md" ``` ## Step 8: Return Structured Result diff --git a/agents/gsd-planner.md b/agents/gsd-planner.md index dc7e702a40..9a5a9a20d5 100644 --- a/agents/gsd-planner.md +++ b/agents/gsd-planner.md @@ -1136,7 +1136,7 @@ Plans: ```bash -gsd-sdk query commit "docs($PHASE): create phase plan" \ +gsd-sdk query commit "docs($PHASE): create phase plan" --files \ .planning/phases/$PHASE-*/$PHASE-*-PLAN.md .planning/ROADMAP.md ``` diff --git a/agents/gsd-research-synthesizer.md b/agents/gsd-research-synthesizer.md index e9b9564dd2..16fc2f51d7 100644 --- a/agents/gsd-research-synthesizer.md +++ b/agents/gsd-research-synthesizer.md @@ -139,7 +139,7 @@ Write to `.planning/research/SUMMARY.md` The 4 parallel researcher agents write files but do NOT commit. You commit everything together. ```bash -gsd-sdk query commit "docs: complete project research" .planning/research/ +gsd-sdk query commit "docs: complete project research" --files .planning/research/ ``` ## Step 8: Return Summary diff --git a/agents/gsd-ui-researcher.md b/agents/gsd-ui-researcher.md index 2efca92653..a64e53a5bb 100644 --- a/agents/gsd-ui-researcher.md +++ b/agents/gsd-ui-researcher.md @@ -292,7 +292,7 @@ Fill all sections. Write to `$PHASE_DIR/$PADDED_PHASE-UI-SPEC.md`. ## Step 6: Commit (optional) ```bash -gsd-sdk query commit "docs($PHASE): UI design contract" "$PHASE_DIR/$PADDED_PHASE-UI-SPEC.md" +gsd-sdk query commit "docs($PHASE): UI design contract" --files "$PHASE_DIR/$PADDED_PHASE-UI-SPEC.md" ``` ## Step 7: Return Structured Result diff --git a/commands/gsd/add-backlog.md b/commands/gsd/add-backlog.md index ee6c4ea09d..1127553e32 100644 --- a/commands/gsd/add-backlog.md +++ b/commands/gsd/add-backlog.md @@ -54,7 +54,7 @@ the normal phase sequence and accumulate context over time. 5. **Commit:** ```bash - gsd-sdk query commit "docs: add backlog item ${NEXT} — ${ARGUMENTS}" .planning/ROADMAP.md ".planning/phases/${NEXT}-${SLUG}/.gitkeep" + gsd-sdk query commit "docs: add backlog item ${NEXT} — ${ARGUMENTS}" --files .planning/ROADMAP.md ".planning/phases/${NEXT}-${SLUG}/.gitkeep" ``` 6. **Report:** diff --git a/commands/gsd/review-backlog.md b/commands/gsd/review-backlog.md index 0eb27b69a2..a8188caaac 100644 --- a/commands/gsd/review-backlog.md +++ b/commands/gsd/review-backlog.md @@ -47,7 +47,7 @@ milestone sequence or remove stale entries. 6. **Commit changes:** ```bash - gsd-sdk query commit "docs: review backlog — promoted N, removed M" .planning/ROADMAP.md + gsd-sdk query commit "docs: review backlog — promoted N, removed M" --files .planning/ROADMAP.md ``` 7. **Report summary:** diff --git a/commands/gsd/thread.md b/commands/gsd/thread.md index 023b1590ee..ee10e54b03 100644 --- a/commands/gsd/thread.md +++ b/commands/gsd/thread.md @@ -83,7 +83,7 @@ When SUBCMD=close and SLUG is set (already sanitized): 3. Commit: ```bash - gsd-sdk query commit "docs: resolve thread — {SLUG}" ".planning/threads/{SLUG}.md" + gsd-sdk query commit "docs: resolve thread — {SLUG}" --files ".planning/threads/{SLUG}.md" ``` 4. Print: @@ -191,7 +191,7 @@ updated: {today ISO date} 5. Commit: ```bash - gsd-sdk query commit "docs: create thread — ${ARGUMENTS}" ".planning/threads/${SLUG}.md" + gsd-sdk query commit "docs: create thread — ${ARGUMENTS}" --files ".planning/threads/${SLUG}.md" ``` 6. Report: diff --git a/docs/zh-CN/references/git-integration.md b/docs/zh-CN/references/git-integration.md index a840a0849c..7332d1cab9 100644 --- a/docs/zh-CN/references/git-integration.md +++ b/docs/zh-CN/references/git-integration.md @@ -51,7 +51,7 @@ Phases: 提交内容: ```bash -gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" .planning/ +gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" --files .planning/ ``` @@ -129,7 +129,7 @@ SUMMARY: .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md 提交内容: ```bash -gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md +gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md ``` **注意:** 代码文件不包含 - 已按任务提交。 @@ -149,7 +149,7 @@ Current: [task name] 提交内容: ```bash -gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" .planning/ +gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/ ``` diff --git a/docs/zh-CN/references/git-planning-commit.md b/docs/zh-CN/references/git-planning-commit.md index 701ce35330..1a7618c37c 100644 --- a/docs/zh-CN/references/git-planning-commit.md +++ b/docs/zh-CN/references/git-planning-commit.md @@ -4,12 +4,12 @@ ## 通过 CLI 提交 -先传提交说明,再传文件路径(位置参数)。`commit` 不要使用 `--files`(该标志仅用于 `commit-to-subrepo`)。 +先传提交说明,然后用 `--files` 显式传入文件路径。`commit` 与 `commit-to-subrepo` 都应使用 `--files` 来声明要提交的路径。 对 `.planning/` 文件始终使用此方式 —— 它会自动处理 `commit_docs` 与 gitignore 检查: ```bash -gsd-sdk query commit "docs({scope}): {description}" .planning/STATE.md .planning/ROADMAP.md +gsd-sdk query commit "docs({scope}): {description}" --files .planning/STATE.md .planning/ROADMAP.md ``` 如果 `commit_docs` 为 `false` 或 `.planning/` 被 gitignore,CLI 会返回 `skipped`(带原因)。无需手动条件检查。 @@ -19,7 +19,7 @@ gsd-sdk query commit "docs({scope}): {description}" .planning/STATE.md .planning 将 `.planning/` 文件变更合并到上次提交: ```bash -gsd-sdk query commit "" .planning/codebase/*.md --amend +gsd-sdk query commit "" --files .planning/codebase/*.md --amend ``` ## 提交消息模式 diff --git a/docs/zh-CN/references/planning-config.md b/docs/zh-CN/references/planning-config.md index 6ce0a30fdc..950f4d18e4 100644 --- a/docs/zh-CN/references/planning-config.md +++ b/docs/zh-CN/references/planning-config.md @@ -40,7 +40,7 @@ ```bash # 提交时自动检查 commit_docs + gitignore: -gsd-sdk query commit "docs: update state" .planning/STATE.md +gsd-sdk query commit "docs: update state" --files .planning/STATE.md # 通过 state load 加载配置(返回 JSON): INIT=$(gsd-sdk query state.load) @@ -58,7 +58,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi **通过 CLI 提交(自动处理检查):** ```bash -gsd-sdk query commit "docs: update state" .planning/STATE.md +gsd-sdk query commit "docs: update state" --files .planning/STATE.md ``` CLI 在内部检查 `commit_docs` 配置和 gitignore 状态 —— 无需手动条件判断。 diff --git a/get-shit-done/references/autonomous-smart-discuss.md b/get-shit-done/references/autonomous-smart-discuss.md index cefe5d603e..62156d278b 100644 --- a/get-shit-done/references/autonomous-smart-discuss.md +++ b/get-shit-done/references/autonomous-smart-discuss.md @@ -266,7 +266,7 @@ Write the file. **Commit:** ```bash -gsd-sdk query commit "docs(${PADDED_PHASE}): smart discuss context" "${phase_dir}/${padded_phase}-CONTEXT.md" +gsd-sdk query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md" ``` Display confirmation: diff --git a/get-shit-done/references/git-integration.md b/get-shit-done/references/git-integration.md index 3ac371edbc..4b041b93c6 100644 --- a/get-shit-done/references/git-integration.md +++ b/get-shit-done/references/git-integration.md @@ -51,7 +51,7 @@ Phases: What to commit: ```bash -gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" .planning/ +gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" --files .planning/ ``` @@ -133,7 +133,7 @@ SUMMARY: .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md What to commit: ```bash -gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md +gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md ``` **Note:** Code files NOT included - already committed per-task. @@ -153,7 +153,7 @@ Current: [task name] What to commit: ```bash -gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" .planning/ +gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/ ``` diff --git a/get-shit-done/references/git-planning-commit.md b/get-shit-done/references/git-planning-commit.md index 2609aa1aa9..7082bfedec 100644 --- a/get-shit-done/references/git-planning-commit.md +++ b/get-shit-done/references/git-planning-commit.md @@ -4,12 +4,12 @@ Commit planning artifacts via `gsd-sdk query commit`, which checks `commit_docs` ## Commit via CLI -Pass the message first, then file paths (positional). Do not use `--files` for `commit` (that flag is only for `commit-to-subrepo`). +Pass the message first, then file paths via `--files`. Both `commit` and `commit-to-subrepo` use `--files` to declare the paths to commit. Always use this for `.planning/` files — it handles `commit_docs` and gitignore checks automatically: ```bash -gsd-sdk query commit "docs({scope}): {description}" .planning/STATE.md .planning/ROADMAP.md +gsd-sdk query commit "docs({scope}): {description}" --files .planning/STATE.md .planning/ROADMAP.md ``` The CLI will return `skipped` (with reason) if `commit_docs` is `false` or `.planning/` is gitignored. No manual conditional checks needed. @@ -19,7 +19,7 @@ The CLI will return `skipped` (with reason) if `commit_docs` is `false` or `.pla To fold `.planning/` file changes into the previous commit: ```bash -gsd-sdk query commit "" .planning/codebase/*.md --amend +gsd-sdk query commit "" --files .planning/codebase/*.md --amend ``` ## Commit Message Patterns diff --git a/get-shit-done/references/planner-revision.md b/get-shit-done/references/planner-revision.md index 7a7a796766..c37ea36635 100644 --- a/get-shit-done/references/planner-revision.md +++ b/get-shit-done/references/planner-revision.md @@ -55,7 +55,7 @@ Group by plan, dimension, severity. ### Step 6: Commit ```bash -gsd-sdk query commit "fix($PHASE): revise plans based on checker feedback" .planning/phases/$PHASE-*/$PHASE-*-PLAN.md +gsd-sdk query commit "fix($PHASE): revise plans based on checker feedback" --files .planning/phases/$PHASE-*/$PHASE-*-PLAN.md ``` ### Step 7: Return Revision Summary diff --git a/get-shit-done/references/planning-config.md b/get-shit-done/references/planning-config.md index ad057b3d58..b4e5cca890 100644 --- a/get-shit-done/references/planning-config.md +++ b/get-shit-done/references/planning-config.md @@ -58,7 +58,7 @@ Configuration options for `.planning/` directory behavior. ```bash # Commit with automatic commit_docs + gitignore checks: -gsd-sdk query commit "docs: update state" .planning/STATE.md +gsd-sdk query commit "docs: update state" --files .planning/STATE.md # Load config via state load (returns JSON): INIT=$(gsd-sdk query state.load) @@ -76,7 +76,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi **Commit via CLI (handles checks automatically):** ```bash -gsd-sdk query commit "docs: update state" .planning/STATE.md +gsd-sdk query commit "docs: update state" --files .planning/STATE.md ``` The CLI checks `commit_docs` config and gitignore status internally — no manual conditionals needed. diff --git a/get-shit-done/workflows/add-todo.md b/get-shit-done/workflows/add-todo.md index 439859da1e..6ae69f6854 100644 --- a/get-shit-done/workflows/add-todo.md +++ b/get-shit-done/workflows/add-todo.md @@ -121,7 +121,7 @@ If `.planning/STATE.md` exists: Commit the todo and any updated state: ```bash -gsd-sdk query commit "docs: capture todo - [title]" .planning/todos/pending/[filename] .planning/STATE.md +gsd-sdk query commit "docs: capture todo - [title]" --files .planning/todos/pending/[filename] .planning/STATE.md ``` Tool respects `commit_docs` config and gitignore automatically. diff --git a/get-shit-done/workflows/autonomous.md b/get-shit-done/workflows/autonomous.md index 49fdf430c2..bac30e727d 100644 --- a/get-shit-done/workflows/autonomous.md +++ b/get-shit-done/workflows/autonomous.md @@ -249,7 +249,7 @@ None — discuss phase skipped. Commit the minimal context: ```bash -gsd-sdk query commit "docs(${PADDED_PHASE}): auto-generated context (discuss skipped)" "${phase_dir}/${padded_phase}-CONTEXT.md" +gsd-sdk query commit "docs(${PADDED_PHASE}): auto-generated context (discuss skipped)" --files "${phase_dir}/${padded_phase}-CONTEXT.md" ``` Proceed to 3b. diff --git a/get-shit-done/workflows/check-todos.md b/get-shit-done/workflows/check-todos.md index 0180c404f6..760cb4bbbf 100644 --- a/get-shit-done/workflows/check-todos.md +++ b/get-shit-done/workflows/check-todos.md @@ -157,7 +157,7 @@ If todo was moved to done/, commit the change: ```bash git rm --cached .planning/todos/pending/[filename] 2>/dev/null || true -gsd-sdk query commit "docs: start work on todo - [title]" .planning/todos/completed/[filename] .planning/STATE.md +gsd-sdk query commit "docs: start work on todo - [title]" --files .planning/todos/completed/[filename] .planning/STATE.md ``` Tool respects `commit_docs` config and gitignore automatically. diff --git a/get-shit-done/workflows/cleanup.md b/get-shit-done/workflows/cleanup.md index 534d33842c..d476e82b4b 100644 --- a/get-shit-done/workflows/cleanup.md +++ b/get-shit-done/workflows/cleanup.md @@ -124,7 +124,7 @@ Repeat for all milestones in the cleanup set. Commit the changes: ```bash -gsd-sdk query commit "chore: archive phase directories from completed milestones" .planning/milestones/ .planning/phases/ +gsd-sdk query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/ ``` diff --git a/get-shit-done/workflows/complete-milestone.md b/get-shit-done/workflows/complete-milestone.md index a73b30563c..864227c23e 100644 --- a/get-shit-done/workflows/complete-milestone.md +++ b/get-shit-done/workflows/complete-milestone.md @@ -494,7 +494,7 @@ Append the extracted Backlog content verbatim to the end of the newly written RO **Safety commit — commit archive files BEFORE deleting any originals:** ```bash -gsd-sdk query commit "chore: archive v[X.Y] milestone files" .planning/milestones/v[X.Y]-ROADMAP.md .planning/milestones/v[X.Y]-REQUIREMENTS.md .planning/milestones/v[X.Y]-MILESTONE-AUDIT.md .planning/MILESTONES.md .planning/PROJECT.md .planning/STATE.md .planning/ROADMAP.md +gsd-sdk query commit "chore: archive v[X.Y] milestone files" --files .planning/milestones/v[X.Y]-ROADMAP.md .planning/milestones/v[X.Y]-REQUIREMENTS.md .planning/milestones/v[X.Y]-MILESTONE-AUDIT.md .planning/MILESTONES.md .planning/PROJECT.md .planning/STATE.md .planning/ROADMAP.md ``` This creates a durable checkpoint in git history. If anything fails after this point, the working tree can be reconstructed from git. @@ -563,7 +563,7 @@ If the "## Cross-Milestone Trends" section exists, update the tables with new da **Commit:** ```bash -gsd-sdk query commit "docs: update retrospective for v${VERSION}" .planning/RETROSPECTIVE.md +gsd-sdk query commit "docs: update retrospective for v${VERSION}" --files .planning/RETROSPECTIVE.md ``` diff --git a/get-shit-done/workflows/diagnose-issues.md b/get-shit-done/workflows/diagnose-issues.md index 808acbe511..d81cdafd78 100644 --- a/get-shit-done/workflows/diagnose-issues.md +++ b/get-shit-done/workflows/diagnose-issues.md @@ -179,7 +179,7 @@ Update status in frontmatter to "diagnosed". Commit the updated UAT.md: ```bash -gsd-sdk query commit "docs({phase_num}): add root causes from diagnosis" ".planning/phases/XX-name/{phase_num}-UAT.md" +gsd-sdk query commit "docs({phase_num}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase_num}-UAT.md" ``` diff --git a/get-shit-done/workflows/discuss-phase-assumptions.md b/get-shit-done/workflows/discuss-phase-assumptions.md index 1029bbbfb2..211b0b632d 100644 --- a/get-shit-done/workflows/discuss-phase-assumptions.md +++ b/get-shit-done/workflows/discuss-phase-assumptions.md @@ -552,7 +552,7 @@ Write file. Commit phase context and discussion log: ```bash -gsd-sdk query commit "docs(${padded_phase}): capture phase context (assumptions mode)" "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" +gsd-sdk query commit "docs(${padded_phase}): capture phase context (assumptions mode)" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" ``` Confirm: "Committed: docs(${padded_phase}): capture phase context (assumptions mode)" @@ -570,7 +570,7 @@ gsd-sdk query state.record-session \ Commit STATE.md: ```bash -gsd-sdk query commit "docs(state): record phase ${PHASE} context session" .planning/STATE.md +gsd-sdk query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md ``` diff --git a/get-shit-done/workflows/discuss-phase.md b/get-shit-done/workflows/discuss-phase.md index ac22e62969..57c67b4815 100644 --- a/get-shit-done/workflows/discuss-phase.md +++ b/get-shit-done/workflows/discuss-phase.md @@ -445,7 +445,7 @@ rm -f "${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json" Commit phase context and discussion log: ```bash -gsd-sdk query commit "docs(${padded_phase}): capture phase context" "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" +gsd-sdk query commit "docs(${padded_phase}): capture phase context" --files "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" ``` Confirm: "Committed: docs(${padded_phase}): capture phase context" @@ -459,7 +459,7 @@ gsd-sdk query state.record-session \ --stopped-at "Phase ${PHASE} context gathered" \ --resume-file "${phase_dir}/${padded_phase}-CONTEXT.md" -gsd-sdk query commit "docs(state): record phase ${PHASE} context session" .planning/STATE.md +gsd-sdk query commit "docs(state): record phase ${PHASE} context session" --files .planning/STATE.md ``` diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 7fecfe7604..0a8d66e6eb 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -793,7 +793,7 @@ increases monotonically across waves. `{status}` is `complete` (success), # Only commit tracking files if they actually changed if ! git diff --quiet .planning/ROADMAP.md .planning/STATE.md 2>/dev/null; then - gsd-sdk query commit "docs(phase-${PHASE_NUMBER}): update tracking after wave ${N}" .planning/ROADMAP.md .planning/STATE.md + gsd-sdk query commit "docs(phase-${PHASE_NUMBER}): update tracking after wave ${N}" --files .planning/ROADMAP.md .planning/STATE.md fi elif [ "${TEST_EXIT}" -eq 124 ]; then echo "⚠ Skipping tracking update — test suite timed out. Plans remain in-progress. Run tests manually to confirm." @@ -1143,7 +1143,7 @@ mv .planning/debug/{slug}.md .planning/debug/resolved/ **6. Commit updated artifacts:** ```bash -gsd-sdk query commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md +gsd-sdk query commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" --files .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md ``` @@ -1386,7 +1386,7 @@ blocked: 0 Commit the file: ```bash -gsd-sdk query commit "test({phase_num}): persist human verification items as UAT" "{phase_dir}/{phase_num}-HUMAN-UAT.md" +gsd-sdk query commit "test({phase_num}): persist human verification items as UAT" --files "{phase_dir}/{phase_num}-HUMAN-UAT.md" ``` **Step B: Present to user:** @@ -1458,7 +1458,7 @@ These items are tracked and will appear in `/gsd-progress` and `/gsd-audit-uat`. ``` ```bash -gsd-sdk query commit "docs(phase-{X}): complete phase execution" .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md {phase_dir}/*-VERIFICATION.md +gsd-sdk query commit "docs(phase-{X}): complete phase execution" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md {phase_dir}/*-VERIFICATION.md ``` @@ -1508,7 +1508,7 @@ for TODO_FILE in "$PENDING_DIR"/*.md; do done if [ ${#CLOSED[@]} -gt 0 ]; then - gsd-sdk query commit "docs(phase-${PHASE_NUMBER}): auto-close ${#CLOSED[@]} todo(s) resolved by this phase" .planning/todos/completed/ .planning/STATE.md || true + gsd-sdk query commit "docs(phase-${PHASE_NUMBER}): auto-close ${#CLOSED[@]} todo(s) resolved by this phase" --files .planning/todos/completed/ .planning/STATE.md|| true echo "◆ Closed ${#CLOSED[@]} todo(s) resolved by Phase ${PHASE_NUMBER}:" for f in "${CLOSED[@]}"; do echo " ✓ $f"; done fi @@ -1533,7 +1533,7 @@ PROJECT.md falls behind silently over multiple phases. 5. Commit the change: ```bash -gsd-sdk query commit "docs(phase-{X}): evolve PROJECT.md after phase completion" .planning/PROJECT.md +gsd-sdk query commit "docs(phase-{X}): evolve PROJECT.md after phase completion" --files .planning/PROJECT.md ``` **Skip this step if** `.planning/PROJECT.md` does not exist. diff --git a/get-shit-done/workflows/execute-plan.md b/get-shit-done/workflows/execute-plan.md index 2766d7027b..268f449574 100644 --- a/get-shit-done/workflows/execute-plan.md +++ b/get-shit-done/workflows/execute-plan.md @@ -440,9 +440,9 @@ IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false") # In parallel mode: exclude STATE.md and ROADMAP.md (orchestrator commits these) if [ "$IS_WORKTREE" = "true" ]; then - gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/REQUIREMENTS.md + gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/REQUIREMENTS.md else - gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md + gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md fi ``` @@ -458,7 +458,7 @@ git diff --name-only ${FIRST_TASK}^..HEAD 2>/dev/null || true Update only structural changes: new src/ dir → STRUCTURE.md | deps → STACK.md | file pattern → CONVENTIONS.md | API client → INTEGRATIONS.md | config → STACK.md | renamed → update paths. Skip code-only/bugfix/content changes. ```bash -gsd-sdk query commit "" .planning/codebase/*.md --amend +gsd-sdk query commit "" --files .planning/codebase/*.md --amend ``` diff --git a/get-shit-done/workflows/explore.md b/get-shit-done/workflows/explore.md index 4138c3bf5e..0e1d19461c 100644 --- a/get-shit-done/workflows/explore.md +++ b/get-shit-done/workflows/explore.md @@ -115,7 +115,7 @@ For each selected output, write the file: Commit if `commit_docs` is enabled: ```bash -gsd-sdk query commit "docs: capture exploration — {topic_slug}" {file_list} +gsd-sdk query commit "docs: capture exploration — {topic_slug}" --files {file_list} ``` ## Step 6: Close diff --git a/get-shit-done/workflows/import.md b/get-shit-done/workflows/import.md index 5de222c3ac..a6f99aa608 100644 --- a/get-shit-done/workflows/import.md +++ b/get-shit-done/workflows/import.md @@ -220,7 +220,7 @@ Update `.planning/STATE.md` if appropriate (e.g., increment total plan count). Commit the imported plan and updated files: ```bash -gsd-sdk query commit "docs({phase}): import plan from {basename FILEPATH}" .planning/phases/{phase}/{plan}-PLAN.md .planning/ROADMAP.md +gsd-sdk query commit "docs({phase}): import plan from {basename FILEPATH}" --files .planning/phases/{phase}/{plan}-PLAN.md .planning/ROADMAP.md ``` Display completion: diff --git a/get-shit-done/workflows/ingest-docs.md b/get-shit-done/workflows/ingest-docs.md index 24a52b3836..2273ba4ef5 100644 --- a/get-shit-done/workflows/ingest-docs.md +++ b/get-shit-done/workflows/ingest-docs.md @@ -289,7 +289,7 @@ Preview the merge diff to the user and gate via approve-revise-abort before writ Commit the ingest results: ```bash -gsd-sdk query commit "docs: ingest {N} docs from {SCAN_PATH} (#2387)" \ +gsd-sdk query commit "docs: ingest {N} docs from {SCAN_PATH} (#2387)" --files \ .planning/PROJECT.md \ .planning/REQUIREMENTS.md \ .planning/ROADMAP.md \ diff --git a/get-shit-done/workflows/map-codebase.md b/get-shit-done/workflows/map-codebase.md index 40106db29f..ca1fa8b9bb 100644 --- a/get-shit-done/workflows/map-codebase.md +++ b/get-shit-done/workflows/map-codebase.md @@ -378,7 +378,7 @@ Continue to commit_codebase_map. Commit the codebase map: ```bash -gsd-sdk query commit "docs: map existing codebase" .planning/codebase/*.md +gsd-sdk query commit "docs: map existing codebase" --files .planning/codebase/*.md ``` Continue to offer_next. diff --git a/get-shit-done/workflows/milestone-summary.md b/get-shit-done/workflows/milestone-summary.md index ea660a4d39..7d15ff56aa 100644 --- a/get-shit-done/workflows/milestone-summary.md +++ b/get-shit-done/workflows/milestone-summary.md @@ -189,7 +189,7 @@ mkdir -p .planning/reports Write the summary, then commit: ```bash -gsd-sdk query commit "docs(v${VERSION}): generate milestone summary for onboarding" \ +gsd-sdk query commit "docs(v${VERSION}): generate milestone summary for onboarding" --files \ ".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md" ``` diff --git a/get-shit-done/workflows/new-milestone.md b/get-shit-done/workflows/new-milestone.md index 15f54032cb..2bc54825eb 100644 --- a/get-shit-done/workflows/new-milestone.md +++ b/get-shit-done/workflows/new-milestone.md @@ -212,7 +212,7 @@ gsd-sdk query phases.clear --confirm ``` ```bash -gsd-sdk query commit "docs: start milestone v[X.Y] [Name]" .planning/PROJECT.md .planning/STATE.md +gsd-sdk query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md ``` ## 7. Load Context and Resolve Models @@ -444,7 +444,7 @@ If "adjust": Return to scoping. **Commit requirements:** ```bash -gsd-sdk query commit "docs: define milestone v[X.Y] requirements" .planning/REQUIREMENTS.md +gsd-sdk query commit "docs: define milestone v[X.Y] requirements" --files .planning/REQUIREMENTS.md ``` ## 10. Create Roadmap @@ -530,7 +530,7 @@ Success criteria: **Commit roadmap** (after approval): ```bash -gsd-sdk query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md +gsd-sdk query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md ``` ## 10.5. Link Pending Todos to Roadmap Phases @@ -573,7 +573,7 @@ files: [existing] **If any todos were linked:** ```bash -gsd-sdk query commit "docs: tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" .planning/todos/pending/*.md +gsd-sdk query commit "docs: tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" --files .planning/todos/pending/*.md ``` Print a summary: diff --git a/get-shit-done/workflows/new-project.md b/get-shit-done/workflows/new-project.md index fb4131ef6f..cbf3a4096e 100644 --- a/get-shit-done/workflows/new-project.md +++ b/get-shit-done/workflows/new-project.md @@ -236,7 +236,7 @@ gsd-sdk query config-new-project '{"mode":"yolo","granularity":"[selected]","par ```bash mkdir -p .planning -gsd-sdk query commit "chore: add project config" .planning/config.json +gsd-sdk query commit "chore: add project config" --files .planning/config.json ``` **Persist auto-advance chain flag to config (survives context compaction):** @@ -447,7 +447,7 @@ Do not compress. Capture everything gathered. ```bash mkdir -p .planning -gsd-sdk query commit "docs: initialize project" .planning/PROJECT.md +gsd-sdk query commit "docs: initialize project" --files .planning/PROJECT.md ``` ## 5. Workflow Preferences @@ -666,7 +666,7 @@ gsd-sdk query config-new-project '{"mode":"[yolo|interactive]","granularity":"[s **Commit config.json:** ```bash -gsd-sdk query commit "chore: add project config" .planning/config.json +gsd-sdk query commit "chore: add project config" --files .planning/config.json ``` ## 5.1. Sub-Repo Detection @@ -1114,7 +1114,7 @@ If "adjust": Return to scoping. **Commit requirements:** ```bash -gsd-sdk query commit "docs: define v1 requirements" .planning/REQUIREMENTS.md +gsd-sdk query commit "docs: define v1 requirements" --files .planning/REQUIREMENTS.md ``` ## 8. Create Roadmap @@ -1266,7 +1266,7 @@ This ensures new projects get the default GSD workflow-enforcement guidance and **Commit roadmap (after approval or auto mode):** ```bash -gsd-sdk query commit "docs: create roadmap ([N] phases)" .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md "$INSTRUCTION_FILE" +gsd-sdk query commit "docs: create roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md "$INSTRUCTION_FILE" ``` ## 9. Done diff --git a/get-shit-done/workflows/pause-work.md b/get-shit-done/workflows/pause-work.md index f4bb5c7d99..7d7dec9945 100644 --- a/get-shit-done/workflows/pause-work.md +++ b/get-shit-done/workflows/pause-work.md @@ -207,7 +207,7 @@ timestamp=$(gsd-sdk query current-timestamp full --raw) ```bash -gsd-sdk query commit "wip: [context-name] paused at [X]/[Y]" [handoff-path] .planning/HANDOFF.json +gsd-sdk query commit "wip: [context-name] paused at [X]/[Y]" --files [handoff-path] .planning/HANDOFF.json ``` diff --git a/get-shit-done/workflows/plan-milestone-gaps.md b/get-shit-done/workflows/plan-milestone-gaps.md index b87feb8d6e..7f05c2f078 100644 --- a/get-shit-done/workflows/plan-milestone-gaps.md +++ b/get-shit-done/workflows/plan-milestone-gaps.md @@ -146,7 +146,7 @@ mkdir -p ".planning/phases/{NN}-{name}" ## 9. Commit Roadmap and Requirements Update ```bash -gsd-sdk query commit "docs(roadmap): add gap closure phases {N}-{M}" .planning/ROADMAP.md .planning/REQUIREMENTS.md +gsd-sdk query commit "docs(roadmap): add gap closure phases {N}-{M}" --files .planning/ROADMAP.md .planning/REQUIREMENTS.md ``` ## 10. Offer Next Steps diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index 06f8b6e23e..88efcc982f 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -201,7 +201,7 @@ Use full relative paths. Group by topic area.] 5. Commit: ```bash -gsd-sdk query commit "docs(${padded_phase}): generate context from PRD" "${phase_dir}/${padded_phase}-CONTEXT.md" +gsd-sdk query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md" ``` 6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research). @@ -925,7 +925,7 @@ For each plan entry extracted from `PLAN-OUTLINE.md`: 5. **Commit per-plan:** ```bash - gsd-sdk query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" "${PHASE_DIR}/${plan_id}-PLAN.md" + gsd-sdk query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" --files "${PHASE_DIR}/${plan_id}-PLAN.md" ``` After all N plans are written and committed, treat this as `## PLANNING COMPLETE` and continue @@ -1263,7 +1263,7 @@ After the script returns, check that the bounced file still has valid YAML front 6. **Commit surviving bounced plans:** If at least one plan survived both the frontmatter validation and the checker re-run, commit the changes: ```bash -gsd-sdk query commit "refactor(${padded_phase}): bounce plans through external refinement" "${PHASE_DIR}/*-PLAN.md" +gsd-sdk query commit "refactor(${padded_phase}): bounce plans through external refinement" --files "${PHASE_DIR}/*-PLAN.md" ``` Display summary: diff --git a/get-shit-done/workflows/plant-seed.md b/get-shit-done/workflows/plant-seed.md index 85c463f3dd..41e5850d6a 100644 --- a/get-shit-done/workflows/plant-seed.md +++ b/get-shit-done/workflows/plant-seed.md @@ -143,7 +143,7 @@ Related code and decisions found in the current codebase: ```bash -gsd-sdk query commit "docs: plant seed — {$IDEA}" .planning/seeds/SEED-{PADDED}-{slug}.md +gsd-sdk query commit "docs: plant seed — {$IDEA}" --files .planning/seeds/SEED-{PADDED}-{slug}.md ``` diff --git a/get-shit-done/workflows/quick.md b/get-shit-done/workflows/quick.md index 054b005ef3..bb4301e16e 100644 --- a/get-shit-done/workflows/quick.md +++ b/get-shit-done/workflows/quick.md @@ -919,7 +919,7 @@ if [ "$COMMIT_DOCS" = "false" ]; then else git add ${file_list} 2>/dev/null fi -gsd-sdk query commit "docs(quick-${quick_id}): ${DESCRIPTION}" ${file_list} +gsd-sdk query commit "docs(quick-${quick_id}): ${DESCRIPTION}" --files ${file_list} ``` Get final commit hash: diff --git a/get-shit-done/workflows/remove-phase.md b/get-shit-done/workflows/remove-phase.md index 0da8041422..645c115502 100644 --- a/get-shit-done/workflows/remove-phase.md +++ b/get-shit-done/workflows/remove-phase.md @@ -103,7 +103,7 @@ Extract from result: `removed`, `directory_deleted`, `renamed_directories`, `ren Stage and commit the removal: ```bash -gsd-sdk query commit "chore: remove phase {target} ({original-phase-name})" .planning/ +gsd-sdk query commit "chore: remove phase {target} ({original-phase-name})" --files .planning/ ``` The commit message preserves the historical record of what was removed. diff --git a/get-shit-done/workflows/review.md b/get-shit-done/workflows/review.md index 7071e9144d..8284055827 100644 --- a/get-shit-done/workflows/review.md +++ b/get-shit-done/workflows/review.md @@ -410,7 +410,7 @@ plans_reviewed: [{list of PLAN.md files}] Commit: ```bash -gsd-sdk query commit "docs: cross-AI review for phase {N}" {phase_dir}/{padded_phase}-REVIEWS.md +gsd-sdk query commit "docs: cross-AI review for phase {N}" --files {phase_dir}/{padded_phase}-REVIEWS.md ``` diff --git a/get-shit-done/workflows/ship.md b/get-shit-done/workflows/ship.md index 947c64cec4..5cc276df67 100644 --- a/get-shit-done/workflows/ship.md +++ b/get-shit-done/workflows/ship.md @@ -257,7 +257,7 @@ gsd-sdk query state.update "Status" "Phase ${PHASE_NUMBER} shipped — PR #${PR_ If `commit_docs` is true: ```bash -gsd-sdk query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #${PR_NUMBER}" .planning/STATE.md +gsd-sdk query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #${PR_NUMBER}" --files .planning/STATE.md ``` diff --git a/get-shit-done/workflows/sketch-wrap-up.md b/get-shit-done/workflows/sketch-wrap-up.md index 6ae4bbf7b4..6edf9d39a1 100644 --- a/get-shit-done/workflows/sketch-wrap-up.md +++ b/get-shit-done/workflows/sketch-wrap-up.md @@ -232,7 +232,7 @@ If this routing line already exists (append mode), leave it as-is. Commit all artifacts (if `COMMIT_DOCS` is true): ```bash -gsd-sdk query commit "docs(sketch-wrap-up): package [N] sketch findings into project skill" .planning/sketches/WRAP-UP-SUMMARY.md +gsd-sdk query commit "docs(sketch-wrap-up): package [N] sketch findings into project skill" --files .planning/sketches/WRAP-UP-SUMMARY.md ``` diff --git a/get-shit-done/workflows/sketch.md b/get-shit-done/workflows/sketch.md index 335d693620..151b5433e6 100644 --- a/get-shit-done/workflows/sketch.md +++ b/get-shit-done/workflows/sketch.md @@ -296,7 +296,7 @@ Iterate until satisfied. **h.** Commit (if `COMMIT_DOCS` is true): ```bash -gsd-sdk query commit "docs(sketch-NNN): [winning direction] — [key visual insight]" .planning/sketches/NNN-descriptive-name/ .planning/sketches/MANIFEST.md +gsd-sdk query commit "docs(sketch-NNN): [winning direction] — [key visual insight]" --files .planning/sketches/NNN-descriptive-name/ .planning/sketches/MANIFEST.md ``` **i.** Report: diff --git a/get-shit-done/workflows/spike-wrap-up.md b/get-shit-done/workflows/spike-wrap-up.md index 46c03aeb84..fd72b4d02d 100644 --- a/get-shit-done/workflows/spike-wrap-up.md +++ b/get-shit-done/workflows/spike-wrap-up.md @@ -246,7 +246,7 @@ Patterns and stack choices established across spike sessions. New spikes follow Commit all artifacts (if `COMMIT_DOCS` is true): ```bash -gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into project skill" .planning/spikes/WRAP-UP-SUMMARY.md .planning/spikes/CONVENTIONS.md +gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into project skill" --files .planning/spikes/WRAP-UP-SUMMARY.md .planning/spikes/CONVENTIONS.md ``` diff --git a/get-shit-done/workflows/spike.md b/get-shit-done/workflows/spike.md index 7204b1a2a6..a91677966e 100644 --- a/get-shit-done/workflows/spike.md +++ b/get-shit-done/workflows/spike.md @@ -334,7 +334,7 @@ tags: [tag1, tag2] **i.** Commit (if `COMMIT_DOCS` is true): ```bash -gsd-sdk query commit "docs(spike-NNN): [VERDICT] — [key finding]" .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md +gsd-sdk query commit "docs(spike-NNN): [VERDICT] — [key finding]" --files .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md ``` **j.** Report: @@ -388,7 +388,7 @@ Only include patterns that repeated across 2+ spikes or were explicitly chosen b Commit (if `COMMIT_DOCS` is true): ```bash -gsd-sdk query commit "docs(spikes): update conventions" .planning/spikes/CONVENTIONS.md +gsd-sdk query commit "docs(spikes): update conventions" --files .planning/spikes/CONVENTIONS.md ``` diff --git a/get-shit-done/workflows/ui-phase.md b/get-shit-done/workflows/ui-phase.md index dfbf5c3c3e..c404e1aa70 100644 --- a/get-shit-done/workflows/ui-phase.md +++ b/get-shit-done/workflows/ui-phase.md @@ -298,7 +298,7 @@ Dimensions: 6/6 passed ## 11. Commit (if configured) ```bash -gsd-sdk query commit "docs(${padded_phase}): UI design contract" "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md" +gsd-sdk query commit "docs(${padded_phase}): UI design contract" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-SPEC.md" ``` ## 12. Update State diff --git a/get-shit-done/workflows/ui-review.md b/get-shit-done/workflows/ui-review.md index bb3e529c38..31bdab71fd 100644 --- a/get-shit-done/workflows/ui-review.md +++ b/get-shit-done/workflows/ui-review.md @@ -176,7 +176,7 @@ tools is detected at runtime. ## 5. Commit (if configured) ```bash -gsd-sdk query commit "docs(${padded_phase}): UI audit review" "${PHASE_DIR}/${PADDED_PHASE}-UI-REVIEW.md" +gsd-sdk query commit "docs(${padded_phase}): UI audit review" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-REVIEW.md" ``` diff --git a/get-shit-done/workflows/verify-work.md b/get-shit-done/workflows/verify-work.md index 76cf4b398b..26812ca12f 100644 --- a/get-shit-done/workflows/verify-work.md +++ b/get-shit-done/workflows/verify-work.md @@ -391,7 +391,7 @@ Clear Current Test section: Commit the UAT file: ```bash -gsd-sdk query commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" ".planning/phases/XX-name/{phase_num}-UAT.md" +gsd-sdk query commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase_num}-UAT.md" ``` Present summary: diff --git a/tests/bug-2767-gsd-sdk-commit-files-flag.test.cjs b/tests/bug-2767-gsd-sdk-commit-files-flag.test.cjs new file mode 100644 index 0000000000..81707fd5fd --- /dev/null +++ b/tests/bug-2767-gsd-sdk-commit-files-flag.test.cjs @@ -0,0 +1,302 @@ +/** + * Bug #2767: Workflows pass paths positionally to `gsd-sdk query commit`. + * + * Runtime behavior under the buggy form (paths positional, no `--files`): + * 1. positional path tokens are joined into the commit subject (commit.ts:110); and + * 2. `filePaths` is empty, so the handler falls back to staging `.planning/` + * wholesale (commit.ts:136), silently swapping the user's intent. + * + * Under the well-formed form (`--files `): + * - subject is the message arg only; + * - exactly the listed files are staged; + * - `commit-to-subrepo` rejects when `--files` is absent (commit.ts:258). + * + * Note: the supplementary doc-lint's `isWellFormed` accepts any invocation that + * has no positional path args after the message — i.e. message-only commits pass + * regardless of any trailing comment. There is no required marker (`# message-only` + * or otherwise); the absence of positional path tokens is the sole signal. + * + * Primary test: invoke the actual `gsd-sdk query commit[-to-subrepo]` binary + * against a real tmp git project and assert the runtime behavior. Supplementary + * test: a doc-lint that scans every shipped .md file to catch regressions of + * the 50-file invocation cleanup landed in this PR. The behavioral tests are + * the contract; the lint is a defense-in-depth guard. + */ + +const { describe, test, beforeEach, afterEach } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const { execFileSync } = require('node:child_process'); +const { createTempGitProject, cleanup } = require('./helpers.cjs'); + +const REPO_ROOT = path.join(__dirname, '..'); +const SDK_CLI = path.join(REPO_ROOT, 'sdk', 'dist', 'cli.js'); + +/** + * Run a git command with hardcoded argv (no shell). Returns trimmed stdout. + */ +function git(projectDir, args) { + return execFileSync('git', args, { cwd: projectDir, encoding: 'utf-8' }).trim(); +} + +/** + * Invoke `gsd-sdk query <...args>` against a project dir. + * Returns { exitCode, stdout, stderr, json } where json is the parsed handler + * payload (the SDK prints a single JSON object to stdout for query handlers). + */ +function runSdkQuery(subcommand, args, projectDir) { + const argv = ['query', subcommand, ...args, '--project-dir', projectDir]; + let stdout = ''; + let stderr = ''; + let exitCode = 0; + try { + stdout = execFileSync(process.execPath, [SDK_CLI, ...argv], { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, GSD_SESSION_KEY: '' }, + }); + } catch (err) { + exitCode = err.status ?? 1; + stdout = err.stdout?.toString() ?? ''; + stderr = err.stderr?.toString() ?? ''; + } + // Extract the trailing JSON object — the CLI prints status lines before it. + let json = null; + const lastBrace = stdout.lastIndexOf('{'); + if (lastBrace >= 0) { + try { json = JSON.parse(stdout.slice(lastBrace).trim()); } catch { /* leave null */ } + if (!json) { + try { json = JSON.parse(stdout.trim()); } catch { /* leave null */ } + } + } + return { exitCode, stdout, stderr, json }; +} + +function gitSubject(projectDir) { + return git(projectDir, ['log', '-1', '--pretty=%s']); +} + +function gitFilesAt(projectDir) { + return git(projectDir, ['show', '--pretty=', '--name-only', 'HEAD']) + .split('\n').filter(Boolean).sort(); +} + +// ─── Behavioral SDK tests ──────────────────────────────────────────────────── + +describe('bug #2767 (behavioral): gsd-sdk query commit --files', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempGitProject('gsd-2767-'); + fs.writeFileSync(path.join(tmpDir, 'foo.md'), 'foo body\n'); + fs.writeFileSync(path.join(tmpDir, 'bar.md'), 'bar body\n'); + // .planning/ change that MUST NOT leak into the commit when --files is used. + fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'state\n'); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('well-formed: --files stages exactly those files with clean subject', () => { + const message = 'test(#2767): well-formed commit'; + const result = runSdkQuery('commit', [message, '--files', 'foo.md', 'bar.md'], tmpDir); + + assert.equal(result.exitCode, 0, `cli failed: ${result.stderr}`); + assert.ok(result.json, `expected JSON body in stdout, got:\n${result.stdout}`); + assert.equal(result.json.committed, true, `commit failed: ${JSON.stringify(result.json)}`); + assert.equal(result.json.message, message, 'subject must be message-only — no path leakage'); + assert.deepEqual(result.json.files.slice().sort(), ['bar.md', 'foo.md']); + + // Cross-check via git itself. + assert.equal(gitSubject(tmpDir), message); + assert.deepEqual(gitFilesAt(tmpDir), ['bar.md', 'foo.md']); + // The .planning/STATE.md change must remain unstaged/unrelated. + const stillDirty = git(tmpDir, ['status', '--porcelain']); + assert.ok(stillDirty.includes('.planning/STATE.md'), + `.planning/STATE.md should remain unstaged, got status:\n${stillDirty}`); + }); + + test('buggy form (positional, no --files): paths leak into subject AND .planning/ fallback fires', () => { + // Documents the misbehavior #2767 prevents at every workflow call site. + // Any future change that makes the buggy form silently "do the right thing" + // trips this test and must justify the change. + const message = 'test(#2767): positional buggy'; + const result = runSdkQuery('commit', [message, 'foo.md', 'bar.md'], tmpDir); + + assert.equal(result.exitCode, 0); + assert.ok(result.json, `expected JSON body, got:\n${result.stdout}`); + assert.equal(result.json.committed, true); + + // Subject got polluted with the path tokens. + assert.equal(result.json.message, `${message} foo.md bar.md`, + 'paths leak into the commit subject under the buggy form (commit.ts:110)'); + assert.equal(gitSubject(tmpDir), `${message} foo.md bar.md`); + + // Fallback staged .planning/STATE.md, NOT foo.md/bar.md. + assert.deepEqual(gitFilesAt(tmpDir), ['.planning/STATE.md'], + 'positional form triggers the .planning/ wholesale fallback (commit.ts:136)'); + const dirty = git(tmpDir, ['status', '--porcelain']); + assert.ok(dirty.includes('foo.md') && dirty.includes('bar.md'), + `foo.md/bar.md should remain unstaged under the buggy form, got:\n${dirty}`); + }); + + test('positional form with no .planning/ change: returns "nothing staged"', () => { + // Reset the .planning/STATE.md change so the fallback has nothing to stage. + fs.rmSync(path.join(tmpDir, '.planning', 'STATE.md'), { force: true }); + + const result = runSdkQuery('commit', ['msg', 'foo.md'], tmpDir); + assert.equal(result.exitCode, 0); + assert.ok(result.json, `expected JSON body, got:\n${result.stdout}`); + assert.equal(result.json.committed, false); + assert.equal(result.json.reason, 'nothing staged', + 'positional form ignores foo.md and finds nothing under .planning/ to fall back to'); + }); +}); + +describe('bug #2767 (behavioral): commit-to-subrepo requires --files', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempGitProject('gsd-2767-sub-'); + fs.writeFileSync( + path.join(tmpDir, '.planning', 'config.json'), + JSON.stringify({ sub_repos: ['vendor/lib'] }, null, 2), + ); + fs.mkdirSync(path.join(tmpDir, 'vendor', 'lib'), { recursive: true }); + fs.writeFileSync(path.join(tmpDir, 'vendor', 'lib', 'README.md'), 'sub\n'); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('rejects with explicit error when --files is omitted', () => { + const result = runSdkQuery('commit-to-subrepo', ['only message, no files'], tmpDir); + assert.equal(result.exitCode, 0); + assert.ok(result.json, `expected JSON body, got:\n${result.stdout}`); + assert.equal(result.json.committed, false); + assert.equal(result.json.reason, '--files required for commit-to-subrepo', + 'commit-to-subrepo enforces --files at runtime (commit.ts:258)'); + }); +}); + +// ─── Supplementary doc-lint (defense-in-depth) ─────────────────────────────── +// +// The behavioral tests above prove the SDK semantics. This lint scans every +// shipped .md invocation to catch regressions in the 50-file workflow cleanup +// landed by this PR — without it, a future contributor adding a new workflow +// could reintroduce the positional form silently. allow-test-rule: doc-text +// invocations cannot be exercised end-to-end (they are agent-prompt strings +// rendered into chat, not invoked by gsd-tools.cjs), so a textual guard is +// the only available enforcement layer. + +const SCAN_DIRS = ['agents', 'commands', 'get-shit-done', 'docs', 'scripts']; +const KNOWN_FLAGS = new Set(['--force', '--amend', '--no-verify', '--files']); + +function walk(dir, out = []) { + if (!fs.existsSync(dir)) return out; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue; + walk(full, out); + } else if (entry.isFile() && entry.name.endsWith('.md')) { + out.push(full); + } + } + return out; +} + +function extractInvocations(filePath) { + const text = fs.readFileSync(filePath, 'utf-8'); + const lines = text.split(/\r?\n/); + const invocations = []; + for (let i = 0; i < lines.length; i++) { + const m = lines[i].match(/(gsd-sdk\s+query\s+commit(?:-to-subrepo)?\b.*)$/); + if (!m) continue; + const tail = m[1].match(/^gsd-sdk\s+query\s+commit(?:-to-subrepo)?(.?)/); + if (tail && tail[1] === '`') continue; + let cmd = m[1]; + let j = i; + while (cmd.endsWith('\\') && j + 1 < lines.length) { + cmd = cmd.slice(0, -1).trimEnd() + ' ' + lines[j + 1].trim(); + j++; + } + invocations.push({ line: i + 1, cmd, file: filePath }); + } + return invocations; +} + +function stripTail(cmd) { + let inSingle = false, inDouble = false; + for (let i = 0; i < cmd.length; i++) { + const c = cmd[i]; + if (c === "'" && !inDouble) inSingle = !inSingle; + else if (c === '"' && !inSingle) inDouble = !inDouble; + else if (!inSingle && !inDouble) { + if (c === '#' && (i === 0 || /\s/.test(cmd[i - 1]))) return cmd.slice(0, i); + if (c === '|' || c === ';' || c === '>' || c === '<') return cmd.slice(0, i); + if (c === '&' && cmd[i + 1] === '&') return cmd.slice(0, i); + } + } + return cmd; +} + +function tokenize(cmd) { + const out = []; + let cur = '', inSingle = false, inDouble = false; + for (let i = 0; i < cmd.length; i++) { + const c = cmd[i]; + if (c === "'" && !inDouble) { inSingle = !inSingle; cur += c; continue; } + if (c === '"' && !inSingle) { inDouble = !inDouble; cur += c; continue; } + if (/\s/.test(c) && !inSingle && !inDouble) { + if (cur) { out.push(cur); cur = ''; } + continue; + } + cur += c; + } + if (cur) out.push(cur); + return out; +} + +function isWellFormed(cmd) { + const truncated = stripTail(cmd); + const parts = tokenize(truncated); + if (parts.length < 3) return true; + const args = parts.slice(3); + if (args.length === 0) return true; + if (args.includes('--files')) return true; + let sawMessage = false; + for (const tok of args) { + if (KNOWN_FLAGS.has(tok)) continue; + if (!sawMessage) { sawMessage = true; continue; } + return false; + } + return true; +} + +describe('bug #2767 (supplementary doc-lint): no positional invocations in shipped .md', () => { + const allFiles = SCAN_DIRS.flatMap(d => walk(path.join(REPO_ROOT, d))); + const allInvocations = allFiles.flatMap(extractInvocations); + + test('lint scanned at least one invocation (sanity)', () => { + assert.ok(allInvocations.length > 0, 'lint scanned 0 invocations — globs are wrong'); + }); + + test('every gsd-sdk query commit invocation either uses --files or has no path args', () => { + const broken = allInvocations.filter(({ cmd }) => !isWellFormed(cmd)); + if (broken.length > 0) { + const detail = broken.map(({ file, line, cmd }) => { + const rel = path.relative(REPO_ROOT, file); + return ` ${rel}:${line}\n ${cmd}`; + }).join('\n'); + assert.fail( + `${broken.length} \`gsd-sdk query commit\` invocation(s) pass paths positionally.\n` + + `The behavioral test above proves what goes wrong at runtime; this lint catches\n` + + `regressions in shipped workflow markdown.\n${detail}` + ); + } + }); +});