lee-spec-kit 0.5.1 → 0.5.2

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/README.en.md CHANGED
@@ -203,9 +203,12 @@ npx lee-spec-kit context F001 --approve A --execute --execute-strict
203
203
  `--json` output includes:
204
204
 
205
205
  - `reasonCode`: status reason code (`SINGLE_MATCHED`, `MULTIPLE_ACTIVE_FEATURES`, etc.)
206
+ - `operationType`: action nature (`local` | `remote` | `manual`)
206
207
  - `actionOptions`: maps labels to atomic actions plus `summary`/`approvalPrompt` for user-facing label explanation
208
+ - `primaryActionLabel` / `primaryActionType` / `primaryActionCategory` / `primaryActionOperationType`: metadata for the first atomic action
209
+ - `selectionFallback`: fallback used when branch auto-detection does not match (`none` | `open_features` | `all_features` | `done_features`)
207
210
  - `workflowPolicy`: current completion policy (`mode`, `requireIssue`, `requireBranch`, `requirePr`, `requireReview`)
208
- - `checkPolicy`: approval validation policy (`token: "<LABEL>"`, `acceptedTokens`, `tokenPattern`, `validLabels`, `requireExplanationBeforeApproval`, `requiredExplanationFields`, `contextVersion`, ...)
211
+ - `checkPolicy`: approval validation policy (`hint`, `policyOnly`, `token: "<LABEL>"`, `acceptedTokens`, `tokenPattern`, `validLabels`, `requireExplanationBeforeApproval`, `requiredExplanationFields`, `contextVersion`, ...)
209
212
 
210
213
  Error payloads (`status: "error"`) include `reasonCode` and labeled `suggestions` (`A/B/C`) (e.g. `INVALID_APPROVAL`, `CONTEXT_STALE`, `EXECUTION_FAILED`, `EXECUTION_NOT_COMMAND`).
211
214
 
package/README.md CHANGED
@@ -222,12 +222,15 @@ npx lee-spec-kit context F001 --approve A --execute --execute-strict
222
222
  - `reasonCode`: 상태 이유 코드 (`SINGLE_MATCHED`, `MULTIPLE_ACTIVE_FEATURES` 등)
223
223
  - `type: "command"`: `scope`(project|docs), `cwd`, `cmd` 제공 (복사하여 붙여넣기 가능한 형태로 `cd ... && git ...` 형태로 출력)
224
224
  - `type: "instruction"`: 사람이 수행해야 하는 안내 메시지
225
+ - `operationType`: 액션 성격 (`local` | `remote` | `manual`)
225
226
  - `actionOptions`: `label`(`A`, `B`, `C`...)과 해당 `action` 매핑 + `summary`/`approvalPrompt`(라벨 설명 템플릿)
227
+ - `primaryActionLabel`/`primaryActionType`/`primaryActionCategory`/`primaryActionOperationType`: 첫 번째 원자 액션의 요약 메타데이터
228
+ - `selectionFallback`: 자동 감지 실패 시 사용된 폴백 (`none` | `open_features` | `all_features` | `done_features`)
226
229
  - `category`: 액션 분류 (자동화/반자동용 `approval.mode: "category"`에서 사용)
227
230
  - `requiresUserCheck`: 사용자 확인 필요 여부 (에이전트는 **사용자 응답을 `<라벨>` 또는 `<라벨> OK` 형식(예: `A`, `A OK`)으로 제한**하는 것을 권장 / 설정의 `approval`로 오버라이드 가능)
228
231
  - `workflowPolicy`: 현재 완료 조건 정책 (`mode`, `requireIssue`, `requireBranch`, `requirePr`, `requireReview`)
229
232
 
230
- 또한 `checkPolicy`가 포함되어, 에이전트가 사용자 확인 정책을 적용할 때 참고할 수 있습니다. (`docPath`, `hint`, `token: "<LABEL>"`, `acceptedTokens`, `tokenPattern`, `validLabels`, `requireExplanationBeforeApproval`, `requiredExplanationFields`, `contextVersion`, `config`)
233
+ 또한 `checkPolicy`가 포함되어, 에이전트가 사용자 확인 정책을 적용할 때 참고할 수 있습니다. (`docPath`, `hint`, `policyOnly`, `token: "<LABEL>"`, `acceptedTokens`, `tokenPattern`, `validLabels`, `requireExplanationBeforeApproval`, `requiredExplanationFields`, `contextVersion`, `config`)
231
234
 
232
235
  오류 응답(`status: "error"`)에는 `reasonCode`와 `suggestions`(라벨형 다음 동작: `A/B/C`)가 포함됩니다. (예: `INVALID_APPROVAL`, `CONTEXT_STALE`, `EXECUTION_FAILED`, `EXECUTION_NOT_COMMAND`)
233
236
 
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ var I18N = {
128
128
  "context.tipShowAll": "\uC804\uCCB4 \uBCF4\uAE30",
129
129
  "context.tipShowDone": "\uC644\uB8CC\uB9CC \uBCF4\uAE30",
130
130
  "context.checkRequired": "[\uD655\uC778 \uD544\uC694] ",
131
- "context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uADDC\uCE59: /docs/agents/agents.md \uCC38\uACE0 (git push/merge/merge commit \uD3EC\uD568) \u2014 [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C `<\uB77C\uBCA8>` \uB610\uB294 `<\uB77C\uBCA8> OK` (\uC608: `A`, `A OK`) \uC751\uB2F5\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589 (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
131
+ "context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45 \uC548\uB0B4(\uD604\uC7AC Next Action \uC544\uB2D8): /docs/agents/agents.md \uCC38\uACE0 (git push/merge/merge commit \uD3EC\uD568). [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C `<\uB77C\uBCA8>` \uB610\uB294 `<\uB77C\uBCA8> OK` (\uC608: `A`, `A OK`) \uC751\uB2F5\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589 (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
132
132
  "context.actionOptionHint": "\uC2B9\uC778 \uC751\uB2F5 \uD615\uC2DD: `<\uB77C\uBCA8>` \uB610\uB294 `<\uB77C\uBCA8> OK` (\uC608: `A`, `A OK`)",
133
133
  "context.actionExplainHint": "\uC2B9\uC778 \uC694\uCCAD \uC804, \uAC01 \uB77C\uBCA8\uC774 \uBB34\uC5C7\uC744 \uC2E4\uD589/\uBCC0\uACBD\uD558\uB294\uC9C0 \uD55C \uC904 \uC694\uC57D\uACFC \uD568\uAED8 \uC124\uBA85\uD558\uC138\uC694.",
134
134
  "context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59: /docs/agents/git-workflow.md \uCC38\uACE0",
@@ -325,7 +325,7 @@ var I18N = {
325
325
  "context.tipShowAll": "Show all",
326
326
  "context.tipShowDone": "Show done only",
327
327
  "context.checkRequired": "[CHECK required] ",
328
- "context.checkPolicyHint": "\u2139\uFE0F User check policy: see /docs/agents/agents.md (includes git push/merge and merge commits) \u2014 if you see [CHECK required], wait for `<label>` or `<label> OK` (e.g. `A`, `A OK`) before proceeding (config: approval can override)",
328
+ "context.checkPolicyHint": "\u2139\uFE0F User check policy notice (not the current next action): see /docs/agents/agents.md (includes git push/merge and merge commits). If you see [CHECK required], wait for `<label>` or `<label> OK` (e.g. `A`, `A OK`) before proceeding (config: approval can override)",
329
329
  "context.actionOptionHint": "Approval reply format: `<label>` or `<label> OK` (e.g. `A`, `A OK`)",
330
330
  "context.actionExplainHint": "Before requesting approval, explain what each label will run/change with a one-line summary.",
331
331
  "context.tipDocsCommitRules": "Commit message rules: /docs/agents/git-workflow.md",
@@ -3869,6 +3869,22 @@ async function runConfig(options) {
3869
3869
  { owner: "config" }
3870
3870
  );
3871
3871
  }
3872
+ var REMOTE_ACTION_CATEGORIES = /* @__PURE__ */ new Set([
3873
+ "issue_create",
3874
+ "pr_create",
3875
+ "pr_status_update",
3876
+ "code_review"
3877
+ ]);
3878
+ var LOCAL_ACTION_CATEGORIES = /* @__PURE__ */ new Set([
3879
+ "docs_commit",
3880
+ "branch_create",
3881
+ "task_execute"
3882
+ ]);
3883
+ var REMOTE_COMMAND_PATTERN = /\b(?:git\s+push|git\s+merge|gh\s+(?:issue|pr)\b)/i;
3884
+ function resolveComponentOption(options) {
3885
+ const component = (options.component || options.repo || "").trim().toLowerCase();
3886
+ return component || void 0;
3887
+ }
3872
3888
  function getActionLabel(index) {
3873
3889
  let n = index + 1;
3874
3890
  let label = "";
@@ -3879,6 +3895,29 @@ function getActionLabel(index) {
3879
3895
  }
3880
3896
  return label;
3881
3897
  }
3898
+ function resolveActionOperationType(action) {
3899
+ if (action.operationType) return action.operationType;
3900
+ if (action.type === "command") {
3901
+ if (REMOTE_COMMAND_PATTERN.test(action.cmd)) return "remote";
3902
+ return "local";
3903
+ }
3904
+ if (action.category && REMOTE_ACTION_CATEGORIES.has(action.category)) {
3905
+ return "remote";
3906
+ }
3907
+ if (action.category && LOCAL_ACTION_CATEGORIES.has(action.category)) {
3908
+ return "local";
3909
+ }
3910
+ return "manual";
3911
+ }
3912
+ function annotateActionOperationType(action) {
3913
+ return {
3914
+ ...action,
3915
+ operationType: resolveActionOperationType(action)
3916
+ };
3917
+ }
3918
+ function annotateActions(actions) {
3919
+ return actions.map((action) => annotateActionOperationType(action));
3920
+ }
3882
3921
  function getActionSummary(action) {
3883
3922
  if (action.category === "docs_commit") return "Commit docs updates";
3884
3923
  if (action.category === "issue_create") return "Create and record issue";
@@ -3898,6 +3937,12 @@ function getActionSummary(action) {
3898
3937
  }
3899
3938
  return action.message;
3900
3939
  }
3940
+ function formatActionSummary(action) {
3941
+ if (action.type === "command") {
3942
+ return `(${action.scope}) ${action.cmd}`;
3943
+ }
3944
+ return action.message;
3945
+ }
3901
3946
  function toActionOptions(actions) {
3902
3947
  return actions.map((action, index) => {
3903
3948
  const label = getActionLabel(index);
@@ -3922,6 +3967,7 @@ function buildActionSnapshot(actionOptions) {
3922
3967
  cwd: action.cwd,
3923
3968
  cmd: action.cmd,
3924
3969
  category: action.category,
3970
+ operationType: action.operationType,
3925
3971
  requiresUserCheck: !!action.requiresUserCheck
3926
3972
  };
3927
3973
  }
@@ -3930,6 +3976,7 @@ function buildActionSnapshot(actionOptions) {
3930
3976
  type: action.type,
3931
3977
  message: action.message,
3932
3978
  category: action.category,
3979
+ operationType: action.operationType,
3933
3980
  requiresUserCheck: !!action.requiresUserCheck
3934
3981
  };
3935
3982
  });
@@ -3944,20 +3991,21 @@ function getContextVersion(feature, actionOptions) {
3944
3991
  });
3945
3992
  return createHash("sha256").update(payload).digest("hex").slice(0, 12);
3946
3993
  }
3947
- function parseApprovalLabel(input) {
3948
- const match = input.trim().match(/^([A-Z]+)(?:\s+OK)?$/i);
3949
- if (!match) return null;
3950
- return match[1].toUpperCase();
3951
- }
3952
- function listLabels(actionOptions) {
3953
- if (actionOptions.length === 0) return "-";
3954
- return actionOptions.map((o) => o.label).join(", ");
3994
+ function matchesFeatureSelector(f, selector) {
3995
+ const s = selector.trim();
3996
+ if (!s) return false;
3997
+ if (f.folderName.toLowerCase() === s.toLowerCase()) return true;
3998
+ if (f.slug.toLowerCase() === s.toLowerCase()) return true;
3999
+ if (f.id && f.id.toLowerCase() === s.toLowerCase()) return true;
4000
+ return false;
3955
4001
  }
3956
- function formatActionSummary(action) {
3957
- if (action.type === "command") {
3958
- return `(${action.scope}) ${action.cmd}`;
3959
- }
3960
- return action.message;
4002
+ function detectFromBranch(branchName, features) {
4003
+ const match = branchName.match(/^feat\/\d+-(.+)$/);
4004
+ if (!match) return [];
4005
+ const detected = match[1];
4006
+ return features.filter(
4007
+ (f) => f.slug.toLowerCase() === detected.toLowerCase() || f.folderName.toLowerCase() === detected.toLowerCase()
4008
+ );
3961
4009
  }
3962
4010
  function toSelectionStatus(features, selectionMode, openFeatures, targetFeatures) {
3963
4011
  const isNoOpen = selectionMode === "open" && features.length > 0 && openFeatures.length === 0;
@@ -3974,15 +4022,9 @@ function toReasonCode(status) {
3974
4022
  if (status === "multiple_active") return "MULTIPLE_ACTIVE_FEATURES";
3975
4023
  return "NO_MATCHED_FEATURES";
3976
4024
  }
3977
- async function resolveContextState(config, featureName, options) {
3978
- if (!config) {
3979
- throw createCliError(
3980
- "CONFIG_NOT_FOUND",
3981
- tr(DEFAULT_LANG, "cli", "common.configNotFound")
3982
- );
3983
- }
4025
+ async function resolveContextSelection(config, featureName, options) {
3984
4026
  const { features, branches, warnings } = await scanFeatures(config);
3985
- const selectedComponent = (options.component || options.repo || "").trim().toLowerCase();
4027
+ const selectedComponent = resolveComponentOption(options);
3986
4028
  const scopedFeatures = selectedComponent ? features.filter((f) => f.type === selectedComponent) : features;
3987
4029
  const doneFeatures = scopedFeatures.filter((f) => f.completion.workflowDone);
3988
4030
  const openFeatures = scopedFeatures.filter((f) => !f.completion.workflowDone);
@@ -3994,6 +4036,7 @@ async function resolveContextState(config, featureName, options) {
3994
4036
  );
3995
4037
  let targetFeatures = [];
3996
4038
  let selectionMode = "explicit";
4039
+ let selectionFallback = "none";
3997
4040
  if (featureName) {
3998
4041
  targetFeatures = scopedFeatures.filter(
3999
4042
  (f) => matchesFeatureSelector(f, featureName)
@@ -4026,15 +4069,19 @@ async function resolveContextState(config, featureName, options) {
4026
4069
  }
4027
4070
  if (targetFeatures.length > 0) {
4028
4071
  selectionMode = "branch";
4072
+ selectionFallback = "none";
4029
4073
  } else if (options.all) {
4030
4074
  targetFeatures = scopedFeatures;
4031
4075
  selectionMode = "all";
4076
+ selectionFallback = "all_features";
4032
4077
  } else if (options.done) {
4033
4078
  targetFeatures = doneFeatures;
4034
4079
  selectionMode = "done";
4080
+ selectionFallback = "done_features";
4035
4081
  } else {
4036
4082
  targetFeatures = openFeatures;
4037
4083
  selectionMode = "open";
4084
+ selectionFallback = "open_features";
4038
4085
  }
4039
4086
  }
4040
4087
  const status = toSelectionStatus(
@@ -4044,7 +4091,7 @@ async function resolveContextState(config, featureName, options) {
4044
4091
  targetFeatures
4045
4092
  );
4046
4093
  const matchedFeature = targetFeatures.length === 1 ? targetFeatures[0] : null;
4047
- const actions = matchedFeature?.actions ?? [];
4094
+ const actions = annotateActions(matchedFeature?.actions ?? []);
4048
4095
  const actionOptions = toActionOptions(actions);
4049
4096
  const contextVersion = getContextVersion(matchedFeature, actionOptions);
4050
4097
  return {
@@ -4056,6 +4103,7 @@ async function resolveContextState(config, featureName, options) {
4056
4103
  inProgressFeatures,
4057
4104
  readyToCloseFeatures,
4058
4105
  selectionMode,
4106
+ selectionFallback,
4059
4107
  targetFeatures,
4060
4108
  status,
4061
4109
  matchedFeature,
@@ -4064,6 +4112,32 @@ async function resolveContextState(config, featureName, options) {
4064
4112
  contextVersion
4065
4113
  };
4066
4114
  }
4115
+
4116
+ // src/commands/context.ts
4117
+ async function resolveContextState(config, featureName, options) {
4118
+ if (!config) {
4119
+ throw createCliError(
4120
+ "CONFIG_NOT_FOUND",
4121
+ tr(DEFAULT_LANG, "cli", "common.configNotFound")
4122
+ );
4123
+ }
4124
+ return resolveContextSelection(config, featureName, options);
4125
+ }
4126
+ function parseApprovalLabel(input) {
4127
+ const match = input.trim().match(/^([A-Z]+)(?:\s+OK)?$/i);
4128
+ if (!match) return null;
4129
+ return match[1].toUpperCase();
4130
+ }
4131
+ function listLabels(actionOptions) {
4132
+ if (actionOptions.length === 0) return "-";
4133
+ return actionOptions.map((o) => o.label).join(", ");
4134
+ }
4135
+ function formatActionSummary2(action) {
4136
+ if (action.type === "command") {
4137
+ return `(${action.scope}) ${action.cmd}`;
4138
+ }
4139
+ return action.message;
4140
+ }
4067
4141
  function executeCommandAction(cmd, jsonMode, cwd) {
4068
4142
  const shellPath = process.env.SHELL || (process.platform === "win32" ? process.env.ComSpec || "cmd.exe" : "/bin/sh");
4069
4143
  if (jsonMode) {
@@ -4122,22 +4196,6 @@ function contextCommand(program2) {
4122
4196
  }
4123
4197
  );
4124
4198
  }
4125
- function matchesFeatureSelector(f, selector) {
4126
- const s = selector.trim();
4127
- if (!s) return false;
4128
- if (f.folderName.toLowerCase() === s.toLowerCase()) return true;
4129
- if (f.slug.toLowerCase() === s.toLowerCase()) return true;
4130
- if (f.id && f.id.toLowerCase() === s.toLowerCase()) return true;
4131
- return false;
4132
- }
4133
- function detectFromBranch(branchName, features) {
4134
- const match = branchName.match(/^feat\/\d+-(.+)$/);
4135
- if (!match) return [];
4136
- const detected = match[1];
4137
- return features.filter(
4138
- (f) => f.slug.toLowerCase() === detected.toLowerCase() || f.folderName.toLowerCase() === detected.toLowerCase()
4139
- );
4140
- }
4141
4199
  function getListLabel(f, stepsMap, lang, workflowPolicy) {
4142
4200
  if (f.completion.implementationDone && !f.completion.workflowDone) {
4143
4201
  if (f.git.docsHasUncommittedChanges) {
@@ -4231,10 +4289,12 @@ async function runContext(featureName, options) {
4231
4289
  return;
4232
4290
  }
4233
4291
  if (options.json) {
4292
+ const primaryAction = state.actionOptions[0] ?? null;
4234
4293
  const result = {
4235
4294
  status: state.status,
4236
4295
  reasonCode: toReasonCode(state.status),
4237
4296
  selectionMode: state.selectionMode,
4297
+ selectionFallback: state.selectionFallback,
4238
4298
  branches: state.branches,
4239
4299
  warnings: state.warnings,
4240
4300
  matchedFeature: state.matchedFeature,
@@ -4246,10 +4306,15 @@ async function runContext(featureName, options) {
4246
4306
  readyToCloseCandidates: state.selectionMode === "open" ? state.readyToCloseFeatures : [],
4247
4307
  actions: state.actions,
4248
4308
  actionOptions: state.actionOptions,
4309
+ primaryActionLabel: primaryAction?.label ?? null,
4310
+ primaryActionType: primaryAction?.action.type ?? null,
4311
+ primaryActionCategory: primaryAction?.action.category ?? null,
4312
+ primaryActionOperationType: primaryAction?.action.operationType ?? null,
4249
4313
  workflowPolicy,
4250
4314
  checkPolicy: {
4251
4315
  docPath: "/docs/agents/agents.md",
4252
4316
  hint: tr(lang, "cli", "context.checkPolicyHint"),
4317
+ policyOnly: true,
4253
4318
  token: "<LABEL>",
4254
4319
  acceptedTokens: ["<LABEL>", "<LABEL> OK"],
4255
4320
  tokenPattern: "^([A-Z]+)(?:\\s+OK)?$",
@@ -4268,7 +4333,8 @@ async function runContext(featureName, options) {
4268
4333
  label: o.label,
4269
4334
  summary: o.summary,
4270
4335
  approvalPrompt: o.approvalPrompt,
4271
- requiresUserCheck: !!o.action.requiresUserCheck
4336
+ requiresUserCheck: !!o.action.requiresUserCheck,
4337
+ operationType: o.action.operationType
4272
4338
  }))
4273
4339
  },
4274
4340
  prPolicy: {
@@ -4476,7 +4542,7 @@ async function runContext(featureName, options) {
4476
4542
  console.log();
4477
4543
  return;
4478
4544
  }
4479
- const actionOptions = toActionOptions(f.actions);
4545
+ const actionOptions = state.actionOptions;
4480
4546
  console.log(chalk6.green(chalk6.bold("\u{1F449} Next Options (Atomic):")));
4481
4547
  let hasDocsCommand = false;
4482
4548
  actionOptions.forEach(({ label, action }) => {
@@ -4562,7 +4628,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
4562
4628
  }
4563
4629
  console.log();
4564
4630
  console.log(chalk6.green(`\u2705 Approved option: ${parsedLabel}`));
4565
- console.log(chalk6.gray(` - Action: ${formatActionSummary(selectedAction)}`));
4631
+ console.log(chalk6.gray(` - Action: ${formatActionSummary2(selectedAction)}`));
4566
4632
  if (selectedAction.type === "command") {
4567
4633
  console.log(chalk6.gray(" - Run with: --execute"));
4568
4634
  } else {
@@ -5230,208 +5296,6 @@ function doctorCommand(program2) {
5230
5296
  }
5231
5297
  });
5232
5298
  }
5233
- function resolveComponentOption(options) {
5234
- const component = (options.component || options.repo || "").trim().toLowerCase();
5235
- return component || void 0;
5236
- }
5237
- function getActionLabel2(index) {
5238
- let n = index + 1;
5239
- let label = "";
5240
- while (n > 0) {
5241
- const rem = (n - 1) % 26;
5242
- label = String.fromCharCode(65 + rem) + label;
5243
- n = Math.floor((n - 1) / 26);
5244
- }
5245
- return label;
5246
- }
5247
- function getActionSummary2(action) {
5248
- if (action.category === "docs_commit") return "Commit docs updates";
5249
- if (action.category === "issue_create") return "Create and record issue";
5250
- if (action.category === "branch_create") return "Create feature branch";
5251
- if (action.category === "pr_create") return "Create PR and record link";
5252
- if (action.category === "pr_status_update") return "Update PR status";
5253
- if (action.category === "code_review") return "Process code review feedback";
5254
- if (action.category === "task_execute") return "Proceed with task execution";
5255
- if (action.category === "feature_done") return "Feature is complete";
5256
- if (action.category === "spec_approve") return "Request spec approval";
5257
- if (action.category === "plan_approve") return "Request plan approval";
5258
- if (action.category === "tasks_approve") return "Request tasks approval";
5259
- if (action.category === "pr_metadata_migrate") return "Update tasks.md to latest PR fields";
5260
- if (action.category === "fallback") return "Re-check context and rerun";
5261
- if (action.type === "command") {
5262
- return action.scope === "docs" ? "Run docs command" : "Run project command";
5263
- }
5264
- return action.message;
5265
- }
5266
- function formatActionSummary2(action) {
5267
- if (action.type === "command") {
5268
- return `(${action.scope}) ${action.cmd}`;
5269
- }
5270
- return action.message;
5271
- }
5272
- function toActionOptions2(actions) {
5273
- return actions.map((action, index) => {
5274
- const label = getActionLabel2(index);
5275
- const summary = getActionSummary2(action);
5276
- const detail = formatActionSummary2(action);
5277
- return {
5278
- label,
5279
- summary,
5280
- detail,
5281
- approvalPrompt: `${label}: ${summary}`,
5282
- action
5283
- };
5284
- });
5285
- }
5286
- function buildActionSnapshot2(actionOptions) {
5287
- return actionOptions.map(({ label, action }) => {
5288
- if (action.type === "command") {
5289
- return {
5290
- label,
5291
- type: action.type,
5292
- scope: action.scope,
5293
- cwd: action.cwd,
5294
- cmd: action.cmd,
5295
- category: action.category,
5296
- requiresUserCheck: !!action.requiresUserCheck
5297
- };
5298
- }
5299
- return {
5300
- label,
5301
- type: action.type,
5302
- message: action.message,
5303
- category: action.category,
5304
- requiresUserCheck: !!action.requiresUserCheck
5305
- };
5306
- });
5307
- }
5308
- function getContextVersion2(feature, actionOptions) {
5309
- if (!feature) return null;
5310
- const payload = JSON.stringify({
5311
- id: feature.id || "",
5312
- folderName: feature.folderName,
5313
- currentStep: feature.currentStep,
5314
- actionSnapshot: buildActionSnapshot2(actionOptions)
5315
- });
5316
- return createHash("sha256").update(payload).digest("hex").slice(0, 12);
5317
- }
5318
- function matchesFeatureSelector2(f, selector) {
5319
- const s = selector.trim();
5320
- if (!s) return false;
5321
- if (f.folderName.toLowerCase() === s.toLowerCase()) return true;
5322
- if (f.slug.toLowerCase() === s.toLowerCase()) return true;
5323
- if (f.id && f.id.toLowerCase() === s.toLowerCase()) return true;
5324
- return false;
5325
- }
5326
- function detectFromBranch2(branchName, features) {
5327
- const match = branchName.match(/^feat\/\d+-(.+)$/);
5328
- if (!match) return [];
5329
- const detected = match[1];
5330
- return features.filter(
5331
- (f) => f.slug.toLowerCase() === detected.toLowerCase() || f.folderName.toLowerCase() === detected.toLowerCase()
5332
- );
5333
- }
5334
- function toSelectionStatus2(features, selectionMode, openFeatures, targetFeatures) {
5335
- const isNoOpen = selectionMode === "open" && features.length > 0 && openFeatures.length === 0;
5336
- if (features.length === 0) return "no_features";
5337
- if (isNoOpen) return "no_open";
5338
- if (targetFeatures.length === 1) return "single_matched";
5339
- if (targetFeatures.length > 1) return "multiple_active";
5340
- return "no_match";
5341
- }
5342
- function toReasonCode2(status) {
5343
- if (status === "no_features") return "NO_FEATURES";
5344
- if (status === "no_open") return "NO_OPEN_FEATURES";
5345
- if (status === "single_matched") return "SINGLE_MATCHED";
5346
- if (status === "multiple_active") return "MULTIPLE_ACTIVE_FEATURES";
5347
- return "NO_MATCHED_FEATURES";
5348
- }
5349
- async function resolveContextSelection(config, featureName, options) {
5350
- const { features, branches, warnings } = await scanFeatures(config);
5351
- const selectedComponent = resolveComponentOption(options);
5352
- const scopedFeatures = selectedComponent ? features.filter((f) => f.type === selectedComponent) : features;
5353
- const doneFeatures = scopedFeatures.filter((f) => f.completion.workflowDone);
5354
- const openFeatures = scopedFeatures.filter((f) => !f.completion.workflowDone);
5355
- const inProgressFeatures = openFeatures.filter(
5356
- (f) => !f.completion.implementationDone
5357
- );
5358
- const readyToCloseFeatures = openFeatures.filter(
5359
- (f) => f.completion.implementationDone
5360
- );
5361
- let targetFeatures = [];
5362
- let selectionMode = "explicit";
5363
- if (featureName) {
5364
- targetFeatures = scopedFeatures.filter(
5365
- (f) => matchesFeatureSelector2(f, featureName)
5366
- );
5367
- selectionMode = "explicit";
5368
- } else {
5369
- if (config.projectType === "single") {
5370
- const branchName = branches.project.single || "";
5371
- targetFeatures = detectFromBranch2(branchName, scopedFeatures);
5372
- } else if (selectedComponent) {
5373
- const branchName = branches.project[selectedComponent] || "";
5374
- targetFeatures = detectFromBranch2(
5375
- branchName,
5376
- scopedFeatures
5377
- );
5378
- } else {
5379
- const matches = [];
5380
- const componentKeys = [...new Set(scopedFeatures.map((f) => f.type))].filter((key) => key !== "single");
5381
- for (const component of componentKeys) {
5382
- const branchName = branches.project[component] || "";
5383
- if (!branchName) continue;
5384
- matches.push(
5385
- ...detectFromBranch2(
5386
- branchName,
5387
- scopedFeatures.filter((f) => f.type === component)
5388
- )
5389
- );
5390
- }
5391
- targetFeatures = matches;
5392
- }
5393
- if (targetFeatures.length > 0) {
5394
- selectionMode = "branch";
5395
- } else if (options.all) {
5396
- targetFeatures = scopedFeatures;
5397
- selectionMode = "all";
5398
- } else if (options.done) {
5399
- targetFeatures = doneFeatures;
5400
- selectionMode = "done";
5401
- } else {
5402
- targetFeatures = openFeatures;
5403
- selectionMode = "open";
5404
- }
5405
- }
5406
- const status = toSelectionStatus2(
5407
- scopedFeatures,
5408
- selectionMode,
5409
- openFeatures,
5410
- targetFeatures
5411
- );
5412
- const matchedFeature = targetFeatures.length === 1 ? targetFeatures[0] : null;
5413
- const actions = matchedFeature?.actions ?? [];
5414
- const actionOptions = toActionOptions2(actions);
5415
- const contextVersion = getContextVersion2(matchedFeature, actionOptions);
5416
- return {
5417
- features: scopedFeatures,
5418
- branches,
5419
- warnings,
5420
- doneFeatures,
5421
- openFeatures,
5422
- inProgressFeatures,
5423
- readyToCloseFeatures,
5424
- selectionMode,
5425
- targetFeatures,
5426
- status,
5427
- matchedFeature,
5428
- actions,
5429
- actionOptions,
5430
- contextVersion
5431
- };
5432
- }
5433
-
5434
- // src/commands/view.ts
5435
5299
  function resolveComponentOption2(options) {
5436
5300
  if (options.repo && options.component && options.repo.trim().toLowerCase() !== options.component.trim().toLowerCase()) {
5437
5301
  throw createCliError(
@@ -5489,8 +5353,9 @@ async function runView(featureName, options) {
5489
5353
  if (options.json) {
5490
5354
  const payload = {
5491
5355
  status: state.status,
5492
- reasonCode: toReasonCode2(state.status),
5356
+ reasonCode: toReasonCode(state.status),
5493
5357
  selectionMode: state.selectionMode,
5358
+ selectionFallback: state.selectionFallback,
5494
5359
  counts: {
5495
5360
  features: state.features.length,
5496
5361
  open: state.openFeatures.length,
@@ -5537,7 +5402,7 @@ async function runView(featureName, options) {
5537
5402
  }
5538
5403
  if (!state.matchedFeature) {
5539
5404
  console.log();
5540
- console.log(chalk6.blue(`Selection: ${state.status} (${toReasonCode2(state.status)})`));
5405
+ console.log(chalk6.blue(`Selection: ${state.status} (${toReasonCode(state.status)})`));
5541
5406
  const rows = state.targetFeatures.length > 0 ? state.targetFeatures : state.features;
5542
5407
  for (const f2 of rows) {
5543
5408
  const statusText = f2.completion.workflowDone ? chalk6.green("WORKFLOW_DONE") : f2.completion.implementationDone ? chalk6.cyan("DONE") : chalk6.yellow("IN_PROGRESS");
@@ -5725,16 +5590,18 @@ async function runFlow(featureName, options) {
5725
5590
  context: {
5726
5591
  before: {
5727
5592
  status: before.status,
5728
- reasonCode: toReasonCode2(before.status),
5593
+ reasonCode: toReasonCode(before.status),
5729
5594
  selectionMode: before.selectionMode,
5595
+ selectionFallback: before.selectionFallback,
5730
5596
  matchedFeature: before.matchedFeature,
5731
5597
  actionOptions: before.actionOptions,
5732
5598
  contextVersion: before.contextVersion
5733
5599
  },
5734
5600
  after: {
5735
5601
  status: after.status,
5736
- reasonCode: toReasonCode2(after.status),
5602
+ reasonCode: toReasonCode(after.status),
5737
5603
  selectionMode: after.selectionMode,
5604
+ selectionFallback: after.selectionFallback,
5738
5605
  matchedFeature: after.matchedFeature,
5739
5606
  actionOptions: after.actionOptions,
5740
5607
  contextVersion: after.contextVersion
@@ -5753,7 +5620,7 @@ async function runFlow(featureName, options) {
5753
5620
  console.log(chalk6.bold("\u{1F501} Flow Summary"));
5754
5621
  console.log(
5755
5622
  chalk6.gray(
5756
- `- Before: ${before.status} (${toReasonCode2(before.status)}) / After: ${after.status} (${toReasonCode2(after.status)})`
5623
+ `- Before: ${before.status} (${toReasonCode(before.status)}) / After: ${after.status} (${toReasonCode(after.status)})`
5757
5624
  )
5758
5625
  );
5759
5626
  if (approvalResult && typeof approvalResult === "object") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {