lee-spec-kit 0.8.4 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1299,7 +1299,7 @@ Before taking the next workflow step:
1299
1299
  9. In standalone mode, use the project repo through its managed feature worktree under the shared workspace \`.worktrees/\` root instead of checking the feature branch out in the main project repo
1300
1300
  10. In standalone mode, do not hand-write \`git worktree add\`; run the exact \`nextAction.command\` from \`workflow-stage\` so the managed workspace path, stale directory cleanup, and \`.env\` / \`.env.*\` copy step stay consistent
1301
1301
  11. Keep docs and code synchronized; if code changes materially, update the active feature docs in the same turn before stopping
1302
- 12. When docs are synced to code, refresh an explicit marker like \`<!-- lee-spec-kit:workflow-sync 2026-04-16T12:34:56.789Z -->\` in the active feature docs (prefer \`tasks.md\` or \`decisions.md\`) so \`workflow-audit\` can prove the sync happened after the latest code change
1302
+ 12. When docs are synced to code, keep exactly one explicit marker like \`<!-- lee-spec-kit:workflow-sync 2026-04-16T12:34:56.789Z -->\` in a single active feature doc (prefer \`tasks.md\` or \`decisions.md\`): replace an existing marker timestamp or remove duplicates instead of appending another marker, so \`workflow-audit\` can prove the sync happened after the latest code change
1303
1303
 
1304
1304
  Approval and remote actions:
1305
1305
 
@@ -1314,7 +1314,7 @@ Approval and remote actions:
1314
1314
  Validation:
1315
1315
 
1316
1316
  - Prefer \`npx lee-spec-kit commit-audit --json\` for commit-time staged docs path validation
1317
- - Prefer \`npx lee-spec-kit workflow-audit --json\` as the default docs-sync validator for Codex hooks and end-of-turn checks; it expects the active feature docs to carry a fresh \`lee-spec-kit:workflow-sync\` marker after meaningful code/doc sync
1317
+ - Prefer \`npx lee-spec-kit workflow-audit --json\` as the default docs-sync validator for Codex hooks and end-of-turn checks; it expects the active feature docs to carry one fresh \`lee-spec-kit:workflow-sync\` marker after meaningful code/doc sync
1318
1318
  `;
1319
1319
  function renderManagedSegment(lang, docsRepo) {
1320
1320
  return `${LEE_SPEC_KIT_AGENTS_BEGIN}
@@ -7887,7 +7887,7 @@ function buildPostMergeCleanupCommand(state) {
7887
7887
  }
7888
7888
  if (state.worktreePath) {
7889
7889
  commandParts.push(
7890
- `if [ -d "${state.worktreePath}" ]; then git -C "${state.projectRootGitCwd}" worktree remove "${state.worktreePath}"; fi`
7890
+ `if [ -d "${state.worktreePath}" ]; then if git -C "${state.worktreePath}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then meaningful_changes=$(git -C "${state.worktreePath}" status --porcelain --untracked-files=normal 2>/dev/null || true); if [ -n "$meaningful_changes" ]; then printf '%s\\n' "Managed worktree has tracked or meaningful untracked changes; refusing cleanup: ${state.worktreePath}" >&2; exit 1; fi; git -C "${state.projectRootGitCwd}" worktree remove --force "${state.worktreePath}" || { git -C "${state.projectRootGitCwd}" worktree prune; rm -rf "${state.worktreePath}"; }; else leftover_meaningful=$(find "${state.worktreePath}" -mindepth 1 \\( -name ".next" -o -name "node_modules" -o -name "storybook-static" -o -name "dist" -o -name "build" -o -name "coverage" -o -name ".turbo" -o -name ".cache" \\) -prune -o -print -quit); if [ -n "$leftover_meaningful" ]; then printf '%s\\n' "Managed worktree leftover has files outside generated artifact directories; refusing cleanup: ${state.worktreePath}" >&2; exit 1; fi; git -C "${state.projectRootGitCwd}" worktree prune; rm -rf "${state.worktreePath}"; fi; fi`
7891
7891
  );
7892
7892
  }
7893
7893
  if (state.headBranch) {
@@ -7896,7 +7896,7 @@ function buildPostMergeCleanupCommand(state) {
7896
7896
  );
7897
7897
  if (state.hasOriginRemote) {
7898
7898
  commandParts.push(
7899
- `if git -C "${state.projectRootGitCwd}" show-ref --verify --quiet "refs/remotes/origin/${state.headBranch}"; then git -C "${state.projectRootGitCwd}" push origin --delete "${state.headBranch}"; fi`
7899
+ `if git -C "${state.projectRootGitCwd}" show-ref --verify --quiet "refs/remotes/origin/${state.headBranch}"; then HUSKY=0 git -C "${state.projectRootGitCwd}" push origin --delete "${state.headBranch}"; fi`
7900
7900
  );
7901
7901
  commandParts.push(
7902
7902
  `git -C "${state.projectRootGitCwd}" fetch --prune origin`
@@ -8100,14 +8100,15 @@ function resolveCurrentReviewState(tasks, prDraft, remoteReviewState) {
8100
8100
  }
8101
8101
  return "unknown";
8102
8102
  }
8103
- function buildCodeReviewActionOptions(reviewState) {
8103
+ function buildCodeReviewActionOptions(reviewState, reviewSyncCommand = null) {
8104
8104
  if (reviewState === "merged") {
8105
8105
  return [
8106
8106
  buildStageOption(
8107
8107
  "A",
8108
8108
  "A",
8109
8109
  "review_sync_approved",
8110
- "Sync the already-merged PR state into tasks.md and pr.md before closing the feature."
8110
+ "Sync the already-merged PR state into tasks.md and pr.md before closing the feature.",
8111
+ reviewSyncCommand
8111
8112
  ),
8112
8113
  buildStageOption(
8113
8114
  "B",
@@ -8123,7 +8124,8 @@ function buildCodeReviewActionOptions(reviewState) {
8123
8124
  "A",
8124
8125
  "A",
8125
8126
  "review_sync_approved",
8126
- "Sync the approved PR review state into tasks.md and pr.md, then continue to the merge gate."
8127
+ "Sync the approved PR review state into tasks.md and pr.md, then continue to the merge gate.",
8128
+ reviewSyncCommand
8127
8129
  ),
8128
8130
  buildStageOption(
8129
8131
  "B",
@@ -8672,7 +8674,8 @@ Task commit boundary warning: ${describeTaskCommitGateFailure(committedTaskGate)
8672
8674
  if (requirements.requireReview && (!reviewApprovedInDocs || currentReviewState !== "approved")) {
8673
8675
  const reviewFixAllowed = currentReviewState === "changes_requested";
8674
8676
  const reviewApprovalRequired = !reviewFixAllowed;
8675
- const reviewActionOptions = reviewApprovalRequired ? buildCodeReviewActionOptions(currentReviewState) : void 0;
8677
+ const reviewSyncCommand = currentReviewState === "merged" ? `npx lee-spec-kit github pr ${buildFeatureArgs(feature)} --merge --confirm OK` : null;
8678
+ const reviewActionOptions = reviewApprovalRequired ? buildCodeReviewActionOptions(currentReviewState, reviewSyncCommand) : void 0;
8676
8679
  const reviewSummary = currentReviewState === "approved" ? "Record the approved PR review state in tasks.md and pr.md before proceeding to merge." : currentReviewState === "merged" ? "Sync the already-merged PR state into tasks.md and pr.md before marking the workflow as complete." : currentReviewState === "changes_requested" ? "Address the requested review changes and update the PR review evidence/decision before continuing." : currentReviewState === "review_pending_latest_commit" ? "Wait for a fresh review on the latest PR commit before taking the next review action." : currentReviewState === "review_rate_limited" ? "Wait for the current CodeRabbit review rate limit to clear, then re-check the latest PR review state before continuing." : currentReviewState === "draft" ? "Resolve the draft PR state before continuing to the merge boundary." : currentReviewState === "merge_blocked" ? "Resolve the current PR merge blocker before continuing to merge." : "Wait for PR review or inspect the current review state before taking the next review action.";
8677
8680
  return {
8678
8681
  status: "ok",
@@ -8683,7 +8686,8 @@ Task commit boundary warning: ${describeTaskCommitGateFailure(committedTaskGate)
8683
8686
  nextAction: buildAction(
8684
8687
  "code_review",
8685
8688
  reviewSummary,
8686
- reviewApprovalRequired
8689
+ reviewApprovalRequired,
8690
+ reviewSyncCommand
8687
8691
  ),
8688
8692
  approvalRequired: reviewApprovalRequired,
8689
8693
  implementationAllowed: reviewFixAllowed,
@@ -9105,7 +9109,20 @@ async function collectWorkflowAudit(cwd) {
9105
9109
  const allMeaningfulFeatureDocPaths = meaningfulChangedFeatureDocPaths;
9106
9110
  const latestCodeChangeAt = await getLatestMtimeIso(combinedChangedCodePaths);
9107
9111
  const latestFeatureDocSyncAt = await getLatestWorkflowSyncMarkerAt(activeFeature);
9112
+ const duplicateWorkflowSyncMarkerPaths = await collectDuplicateWorkflowSyncMarkerPaths(activeFeature);
9108
9113
  if (combinedChangedCodePaths.length === 0) {
9114
+ if (duplicateWorkflowSyncMarkerPaths.length > 0) {
9115
+ return {
9116
+ status: "needs_sync",
9117
+ reasonCode: "DUPLICATE_WORKFLOW_SYNC_MARKERS",
9118
+ docsDir: config.docsDir,
9119
+ activeFeatureRef,
9120
+ changedCodePaths: [],
9121
+ changedFeatureDocPaths: duplicateWorkflowSyncMarkerPaths,
9122
+ latestCodeChangeAt: null,
9123
+ latestFeatureDocSyncAt
9124
+ };
9125
+ }
9109
9126
  return {
9110
9127
  status: "ok",
9111
9128
  reasonCode: "WORKFLOW_IN_SYNC",
@@ -9117,6 +9134,18 @@ async function collectWorkflowAudit(cwd) {
9117
9134
  latestFeatureDocSyncAt
9118
9135
  };
9119
9136
  }
9137
+ if (duplicateWorkflowSyncMarkerPaths.length > 0) {
9138
+ return {
9139
+ status: "needs_sync",
9140
+ reasonCode: "DUPLICATE_WORKFLOW_SYNC_MARKERS",
9141
+ docsDir: config.docsDir,
9142
+ activeFeatureRef,
9143
+ changedCodePaths: combinedChangedCodePaths.map((item) => item.relativeToRepo),
9144
+ changedFeatureDocPaths: duplicateWorkflowSyncMarkerPaths,
9145
+ latestCodeChangeAt,
9146
+ latestFeatureDocSyncAt
9147
+ };
9148
+ }
9120
9149
  if (!activeFeatureRef) {
9121
9150
  return {
9122
9151
  status: "needs_sync",
@@ -9242,6 +9271,23 @@ async function getLatestWorkflowSyncMarkerAt(activeFeature) {
9242
9271
  }
9243
9272
  return latest > 0 ? new Date(latest).toISOString() : null;
9244
9273
  }
9274
+ async function collectDuplicateWorkflowSyncMarkerPaths(activeFeature) {
9275
+ if (!activeFeature) return [];
9276
+ const canonicalFiles = ["spec.md", "plan.md", "tasks.md", "decisions.md", "issue.md", "pr.md"];
9277
+ const markerPaths = [];
9278
+ let markerCount = 0;
9279
+ for (const fileName of canonicalFiles) {
9280
+ const absolutePath = path8.join(activeFeature.path, fileName);
9281
+ if (!await fs.pathExists(absolutePath)) continue;
9282
+ const content = await fs.readFile(absolutePath, "utf-8");
9283
+ const matches = [...content.matchAll(WORKFLOW_SYNC_MARKER_PATTERN)];
9284
+ if (matches.length > 0) {
9285
+ markerCount += matches.length;
9286
+ markerPaths.push(normalizeSlashes2(path8.relative(activeFeature.path, absolutePath)));
9287
+ }
9288
+ }
9289
+ return markerCount > 1 ? markerPaths : [];
9290
+ }
9245
9291
  function extractWorkflowSyncMarkerTimes(content, nowMs, fileMtimeMs) {
9246
9292
  const values = [];
9247
9293
  for (const match of content.matchAll(WORKFLOW_SYNC_MARKER_PATTERN)) {