codexspec 0.7.6__tar.gz → 0.7.8__tar.gz

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.
Files changed (67) hide show
  1. {codexspec-0.7.6 → codexspec-0.7.8}/PKG-INFO +11 -2
  2. {codexspec-0.7.6 → codexspec-0.7.8}/README.md +9 -0
  3. {codexspec-0.7.6 → codexspec-0.7.8}/pyproject.toml +1 -1
  4. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/__init__.py +160 -10
  5. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/commands/installer.py +24 -3
  6. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/integrations/codex.py +7 -0
  7. codexspec-0.7.8/src/codexspec/profile.py +112 -0
  8. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/commit-staged.md +10 -0
  9. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/config.md +24 -0
  10. codexspec-0.7.8/templates/commands/debug.md +80 -0
  11. codexspec-0.7.8/templates/commands/distill.md +111 -0
  12. codexspec-0.7.8/templates/commands/evolve.md +74 -0
  13. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/implement-tasks.md +33 -1
  14. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/pr.md +10 -0
  15. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/specify.md +9 -0
  16. {codexspec-0.7.6 → codexspec-0.7.8}/.gitignore +0 -0
  17. {codexspec-0.7.6 → codexspec-0.7.8}/LICENSE +0 -0
  18. {codexspec-0.7.6 → codexspec-0.7.8}/codexspec-icon.svg +0 -0
  19. {codexspec-0.7.6 → codexspec-0.7.8}/codexspec-logo-dark.svg +0 -0
  20. {codexspec-0.7.6 → codexspec-0.7.8}/codexspec-logo-light.svg +0 -0
  21. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/check-i18n-completeness.sh +0 -0
  22. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/check-i18n-structure.sh +0 -0
  23. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/check-prerequisites.sh +0 -0
  24. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/common.sh +0 -0
  25. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/create-new-feature.sh +0 -0
  26. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/bash/review-context.sh +0 -0
  27. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/powershell/check-prerequisites.ps1 +0 -0
  28. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/powershell/common.ps1 +0 -0
  29. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/powershell/create-new-feature.ps1 +0 -0
  30. {codexspec-0.7.6 → codexspec-0.7.8}/scripts/powershell/review-context.ps1 +0 -0
  31. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/commands/__init__.py +0 -0
  32. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/i18n.py +0 -0
  33. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/idea.md +0 -0
  34. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/integrations/__init__.py +0 -0
  35. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/integrations/base.py +0 -0
  36. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/integrations/claude.py +0 -0
  37. {codexspec-0.7.6 → codexspec-0.7.8}/src/codexspec/translator.py +0 -0
  38. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/analyze.md +0 -0
  39. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/checklist.md +0 -0
  40. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/clarify.md +0 -0
  41. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/constitution.md +0 -0
  42. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/generate-spec.md +0 -0
  43. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/plan-to-tasks.md +0 -0
  44. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/quick.md +0 -0
  45. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/review-code.md +0 -0
  46. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/review-plan.md +0 -0
  47. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/review-spec.md +0 -0
  48. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/review-tasks.md +0 -0
  49. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/spec-to-plan.md +0 -0
  50. {codexspec-0.7.6 → codexspec-0.7.8}/templates/commands/tasks-to-issues.md +0 -0
  51. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/checklist-template.md +0 -0
  52. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/constitution-template.md +0 -0
  53. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/plan-template-detailed.md +0 -0
  54. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/plan-template-simple.md +0 -0
  55. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/requirements-template.md +0 -0
  56. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/spec-template-detailed.md +0 -0
  57. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/spec-template-simple.md +0 -0
  58. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/tasks-template-detailed.md +0 -0
  59. {codexspec-0.7.6 → codexspec-0.7.8}/templates/docs/tasks-template-simple.md +0 -0
  60. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/de.json +0 -0
  61. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/en.json +0 -0
  62. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/es.json +0 -0
  63. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/fr.json +0 -0
  64. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/ja.json +0 -0
  65. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/ko.json +0 -0
  66. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/pt-BR.json +0 -0
  67. {codexspec-0.7.6 → codexspec-0.7.8}/templates/translations/zh-CN.json +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: codexspec
3
- Version: 0.7.6
3
+ Version: 0.7.8
4
4
  Summary: CodexSpec - A Requirements-First SDD toolkit for Claude Code
5
5
  Project-URL: Homepage, https://github.com/Zts0hg/codexspec
6
6
  Project-URL: Repository, https://github.com/Zts0hg/codexspec
@@ -555,6 +555,7 @@ Implementation follows **conditional TDD workflow**:
555
555
  | `--set-commit-lang`, `-c` | Set the commit-message language |
556
556
  | `--list-langs` | List all supported languages |
557
557
  | `--auto-next` | Toggle/set `workflow.auto_next` (bare toggles; or on/off) |
558
+ | `--auto-distill` | Toggle/set `workflow.auto_distill` (default on; bare toggles; or on/off) |
558
559
 
559
560
  </details>
560
561
 
@@ -588,6 +589,14 @@ Implementation follows **conditional TDD workflow**:
588
589
  | `/codexspec:analyze` | Cross-artifact consistency analysis (auto-remediating, severity-based) |
589
590
  | `/codexspec:checklist` | Generate requirements quality checklist |
590
591
  | `/codexspec:tasks-to-issues` | Convert tasks to GitHub Issues |
592
+ | `/codexspec:debug` | Systematic root-cause debugging (4 phases; standalone or escalated from implement-tasks) |
593
+
594
+ #### Self-Evolution Commands
595
+
596
+ | Command | Description |
597
+ | --- | --- |
598
+ | `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
599
+ | `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
591
600
 
592
601
  #### Git Workflow Commands
593
602
 
@@ -510,6 +510,7 @@ Implementation follows **conditional TDD workflow**:
510
510
  | `--set-commit-lang`, `-c` | Set the commit-message language |
511
511
  | `--list-langs` | List all supported languages |
512
512
  | `--auto-next` | Toggle/set `workflow.auto_next` (bare toggles; or on/off) |
513
+ | `--auto-distill` | Toggle/set `workflow.auto_distill` (default on; bare toggles; or on/off) |
513
514
 
514
515
  </details>
515
516
 
@@ -543,6 +544,14 @@ Implementation follows **conditional TDD workflow**:
543
544
  | `/codexspec:analyze` | Cross-artifact consistency analysis (auto-remediating, severity-based) |
544
545
  | `/codexspec:checklist` | Generate requirements quality checklist |
545
546
  | `/codexspec:tasks-to-issues` | Convert tasks to GitHub Issues |
547
+ | `/codexspec:debug` | Systematic root-cause debugging (4 phases; standalone or escalated from implement-tasks) |
548
+
549
+ #### Self-Evolution Commands
550
+
551
+ | Command | Description |
552
+ | --- | --- |
553
+ | `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
554
+ | `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
546
555
 
547
556
  #### Git Workflow Commands
548
557
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codexspec"
3
- version = "0.7.6"
3
+ version = "0.7.8"
4
4
  description = "CodexSpec - A Requirements-First SDD toolkit for Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -40,10 +40,11 @@ from .i18n import (
40
40
  update_language_field,
41
41
  )
42
42
  from .integrations import get_integrations
43
+ from .profile import ensure_profile_scaffold, inject_profile_block
43
44
  from .translator import SUPPORTED_LANGUAGES, translate
44
45
 
45
46
  # Version info
46
- __version__ = "0.7.0"
47
+ __version__ = "0.7.8"
47
48
  __author__ = "CodexSpec Team"
48
49
 
49
50
  # Constitution file path constants
@@ -181,6 +182,11 @@ _AUTO_NEXT_FALSE = {"off", "false", "0", "no"}
181
182
  _AUTO_NEXT_SENTINEL = "__toggle__"
182
183
  _AUTO_NEXT_ACCEPTED = "on/off, true/false, 1/0, yes/no"
183
184
 
185
+ # workflow.auto_distill toggle. Unlike auto_next, auto_distill defaults to ON
186
+ # (opt-out): only the literal ``false`` disables it. Reuses the auto_next token
187
+ # sets for parsing; a distinct sentinel keeps the bare-toggle rewrites separate.
188
+ _AUTO_DISTILL_SENTINEL = "__toggle_distill__"
189
+
184
190
 
185
191
  @app.command()
186
192
  def config(
@@ -221,6 +227,13 @@ def config(
221
227
  "--auto-next",
222
228
  help="Toggle workflow.auto_next (bare), or set it (on/off|true/false|1/0|yes/no).",
223
229
  ),
230
+ # ``--auto-distill`` mirrors ``--auto-next``; the bare toggle is rewritten to
231
+ # ``--auto-distill=<sentinel>`` by ``_normalize_auto_distill_argv`` in ``main()``.
232
+ auto_distill: Optional[str] = typer.Option(
233
+ None,
234
+ "--auto-distill",
235
+ help="Toggle workflow.auto_distill (bare), or set it (on/off|true/false|1/0|yes/no).",
236
+ ),
224
237
  ) -> None:
225
238
  """
226
239
  View or modify CodexSpec project configuration.
@@ -233,6 +246,7 @@ def config(
233
246
  codexspec config --set-lang zh-CN # Set language to Chinese
234
247
  codexspec config --set-commit-lang en # Set commit messages to English
235
248
  codexspec config --auto-next # Toggle workflow.auto_next
249
+ codexspec config --auto-distill off # Disable workflow.auto_distill (default on)
236
250
  codexspec config --list-langs # List supported languages
237
251
  """
238
252
  # Handle list languages
@@ -274,6 +288,25 @@ def config(
274
288
  raise typer.Exit(1)
275
289
  return
276
290
 
291
+ # Handle auto-distill toggle/set
292
+ if auto_distill is not None:
293
+ if auto_distill == _AUTO_DISTILL_SENTINEL:
294
+ target = not _read_auto_distill(config_file)
295
+ else:
296
+ try:
297
+ target = parse_auto_distill_value(auto_distill)
298
+ except ValueError:
299
+ console.print(f"[red]Invalid --auto-distill value:[/red] {auto_distill!r}")
300
+ console.print(f"Accepted values: {_AUTO_NEXT_ACCEPTED} (or pass --auto-distill bare to toggle).")
301
+ raise typer.Exit(1)
302
+ if _write_auto_distill(config_file, target):
303
+ state = "enabled" if target else "disabled"
304
+ console.print(f"[green]auto_distill {state}[/green] (workflow.auto_distill = {str(target).lower()})")
305
+ else:
306
+ console.print("[red]Failed to update workflow.auto_distill[/red]")
307
+ raise typer.Exit(1)
308
+ return
309
+
277
310
  # Handle set language
278
311
  if set_lang:
279
312
  normalized = normalize_locale(set_lang)
@@ -674,6 +707,11 @@ def init(
674
707
  (codexspec_dir / "templates" / "docs").mkdir(exist_ok=True)
675
708
  (codexspec_dir / "scripts").mkdir(exist_ok=True)
676
709
 
710
+ # Ensure the profile scaffold unconditionally (independent of integrations),
711
+ # so knowledge distilled later is effective immediately with no re-init and
712
+ # no dangling reference. Non-destructive: existing profile files are kept.
713
+ ensure_profile_scaffold(target_dir)
714
+
677
715
  # Copy helper scripts based on platform
678
716
  scripts_source_dir = get_scripts_dir()
679
717
  if scripts_source_dir.exists():
@@ -852,6 +890,10 @@ def init(
852
890
  prepend_compliance_section(claude_md)
853
891
  console.print(f"[green]{translate('cli.init.compliance_added', normalized_lang)}[/green]")
854
892
 
893
+ # Inject the profile block AFTER creation/compliance so it never clobbers
894
+ # the compliance @import or the user's body (bounded, idempotent).
895
+ inject_profile_block(claude_md)
896
+
855
897
  # Initialize git if requested
856
898
  if not no_git and not (target_dir / ".git").exists():
857
899
  try:
@@ -1042,6 +1084,101 @@ def _dump_lines(path: Path, lines: list[str]) -> bool:
1042
1084
  return True
1043
1085
 
1044
1086
 
1087
+ # --- workflow.auto_distill toggle helpers ----------------------------------
1088
+
1089
+
1090
+ def parse_auto_distill_value(raw: str) -> bool:
1091
+ """Parse an explicit ``--auto-distill`` value into a boolean.
1092
+
1093
+ Accepts the same tokens as ``--auto-next`` (``on/off``, ``true/false``,
1094
+ ``1/0``, ``yes/no``; case-insensitive, surrounding whitespace ignored).
1095
+ Raises ``ValueError`` for any other token.
1096
+ """
1097
+ token = (raw or "").strip().lower()
1098
+ if token in _AUTO_NEXT_TRUE:
1099
+ return True
1100
+ if token in _AUTO_NEXT_FALSE:
1101
+ return False
1102
+ raise ValueError(f"invalid --auto-distill value: {raw!r}")
1103
+
1104
+
1105
+ def _read_auto_distill(config_file: Path) -> bool:
1106
+ """Return True unless ``workflow.auto_distill`` is the literal ``false``.
1107
+
1108
+ ``auto_distill`` defaults to enabled (opt-out): an absent key/section, an
1109
+ explicit ``true``, or any non-``false`` value enables it; only the literal
1110
+ ``false`` disables it. A missing file also reads as enabled.
1111
+ """
1112
+ try:
1113
+ content = config_file.read_text(encoding="utf-8")
1114
+ except OSError:
1115
+ return True
1116
+ in_workflow = False
1117
+ for line in content.splitlines():
1118
+ if not line.strip():
1119
+ continue
1120
+ if not line[0].isspace(): # top-level key (or comment)
1121
+ key = line.split("#", 1)[0].strip()
1122
+ in_workflow = key == "workflow:"
1123
+ continue
1124
+ if in_workflow:
1125
+ match = re.match(r"^\s*auto_distill:\s*(\S+?)\s*(?:#.*)?$", line)
1126
+ if match:
1127
+ return match.group(1) != "false"
1128
+ return True
1129
+
1130
+
1131
+ def _write_auto_distill(config_file: Path, value: bool) -> bool:
1132
+ """Set ``workflow.auto_distill`` to an unquoted boolean.
1133
+
1134
+ Mirrors ``_write_auto_next``: (1) update the value in place when the key
1135
+ exists under ``workflow:``; (2) insert it as the section's first child when
1136
+ the section exists but the key is absent; (3) append a ``workflow:`` section
1137
+ when absent. Preserves all other lines/comments. Returns ``False`` on I/O
1138
+ error.
1139
+ """
1140
+ try:
1141
+ content = config_file.read_text(encoding="utf-8")
1142
+ except OSError:
1143
+ return False
1144
+
1145
+ token = "true" if value else "false"
1146
+ lines = content.split("\n")
1147
+
1148
+ workflow_idx: Optional[int] = None
1149
+ in_workflow = False
1150
+ for i, line in enumerate(lines):
1151
+ if not line.strip():
1152
+ continue
1153
+ if not line[0].isspace():
1154
+ key = line.split("#", 1)[0].strip()
1155
+ in_workflow = key == "workflow:"
1156
+ if in_workflow:
1157
+ workflow_idx = i
1158
+ continue
1159
+ if in_workflow and re.match(r"^\s*auto_distill:\s*\S+", line):
1160
+ indent = line[: len(line) - len(line.lstrip())]
1161
+ lines[i] = f"{indent}auto_distill: {token}"
1162
+ return _dump_lines(config_file, lines)
1163
+
1164
+ if workflow_idx is not None:
1165
+ lines.insert(workflow_idx + 1, f" auto_distill: {token}")
1166
+ return _dump_lines(config_file, lines)
1167
+
1168
+ section = f"workflow:\n auto_distill: {token}"
1169
+ if not content:
1170
+ new_content = section + "\n"
1171
+ elif content.endswith("\n"):
1172
+ new_content = content + "\n" + section + "\n"
1173
+ else:
1174
+ new_content = content + "\n\n" + section + "\n"
1175
+ try:
1176
+ config_file.write_text(new_content, encoding="utf-8")
1177
+ except OSError:
1178
+ return False
1179
+ return True
1180
+
1181
+
1045
1182
  def _next_step_start(integration_keys: set[str], language: str) -> str:
1046
1183
  """Return a target-aware start instruction."""
1047
1184
  if integration_keys == {"codex"}:
@@ -1439,24 +1576,23 @@ The following slash commands are available in this project:
1439
1576
  """
1440
1577
 
1441
1578
 
1442
- def _normalize_auto_next_argv(argv: list[str]) -> list[str]:
1443
- """Rewrite a bare ``--auto-next`` into ``--auto-next=<sentinel>``.
1579
+ def _normalize_optional_value_argv(argv: list[str], flag: str, sentinel: str) -> list[str]:
1580
+ """Rewrite a bare ``flag`` into ``flag=<sentinel>``.
1444
1581
 
1445
1582
  Click 8.3 no longer honors ``flag_value`` for a bare optional-value option
1446
- (it errors "requires an argument"). To preserve ``codexspec config
1447
- --auto-next`` (bare) as a toggle, a standalone ``--auto-next`` token — one
1448
- that is not already in ``--auto-next=...`` form and is not followed by a
1449
- value token — is rewritten to ``--auto-next=<sentinel>``, which the
1450
- ``config`` handler interprets as a toggle.
1583
+ (it errors "requires an argument"). A standalone ``flag`` token — one that is
1584
+ not already in ``flag=...`` form and is not followed by a value token — is
1585
+ rewritten to ``flag=<sentinel>``, which the ``config`` handler reads as a
1586
+ toggle.
1451
1587
  """
1452
1588
  out: list[str] = []
1453
1589
  i = 0
1454
1590
  while i < len(argv):
1455
1591
  tok = argv[i]
1456
- if tok == "--auto-next":
1592
+ if tok == flag:
1457
1593
  nxt = argv[i + 1] if i + 1 < len(argv) else None
1458
1594
  if nxt is None or nxt.startswith("-"):
1459
- out.append(f"--auto-next={_AUTO_NEXT_SENTINEL}")
1595
+ out.append(f"{flag}={sentinel}")
1460
1596
  else:
1461
1597
  out.append(tok)
1462
1598
  out.append(nxt)
@@ -1467,10 +1603,24 @@ def _normalize_auto_next_argv(argv: list[str]) -> list[str]:
1467
1603
  return out
1468
1604
 
1469
1605
 
1606
+ def _normalize_auto_next_argv(argv: list[str]) -> list[str]:
1607
+ """Rewrite a bare ``--auto-next`` into ``--auto-next=<sentinel>`` (see
1608
+ ``_normalize_optional_value_argv``)."""
1609
+ return _normalize_optional_value_argv(argv, "--auto-next", _AUTO_NEXT_SENTINEL)
1610
+
1611
+
1612
+ def _normalize_auto_distill_argv(argv: list[str]) -> list[str]:
1613
+ """Rewrite a bare ``--auto-distill`` into ``--auto-distill=<sentinel>`` (see
1614
+ ``_normalize_optional_value_argv``)."""
1615
+ return _normalize_optional_value_argv(argv, "--auto-distill", _AUTO_DISTILL_SENTINEL)
1616
+
1617
+
1470
1618
  def main() -> None:
1471
1619
  """Main entry point for the CLI."""
1472
1620
  if "--auto-next" in sys.argv:
1473
1621
  sys.argv = _normalize_auto_next_argv(sys.argv)
1622
+ if "--auto-distill" in sys.argv:
1623
+ sys.argv = _normalize_auto_distill_argv(sys.argv)
1474
1624
  app()
1475
1625
 
1476
1626
 
@@ -47,8 +47,8 @@ def get_commands_metadata() -> list[CommandMetadata]:
47
47
 
48
48
  Returns:
49
49
  List of CommandMetadata dictionaries sorted by category priority:
50
- core (9) -> enhanced (4) -> git (2) -> review (2) -> utility (2)
51
- Total: 19 commands
50
+ core (9) -> enhanced (7) -> git (2) -> review (1) -> utility (2)
51
+ Total: 21 commands
52
52
  """
53
53
  return [
54
54
  # Core Commands (9)
@@ -115,7 +115,7 @@ def get_commands_metadata() -> list[CommandMetadata]:
115
115
  "category": "core",
116
116
  "file_name": "implement-tasks.md",
117
117
  },
118
- # Enhanced Commands (4)
118
+ # Enhanced Commands (7)
119
119
  {
120
120
  "name": "clarify",
121
121
  "display_name": "/codexspec:clarify",
@@ -144,6 +144,27 @@ def get_commands_metadata() -> list[CommandMetadata]:
144
144
  "category": "enhanced",
145
145
  "file_name": "tasks-to-issues.md",
146
146
  },
147
+ {
148
+ "name": "distill",
149
+ "display_name": "/codexspec:distill",
150
+ "description": "从交互中萃取可复用的跨特性知识到 .codexspec/profile/",
151
+ "category": "enhanced",
152
+ "file_name": "distill.md",
153
+ },
154
+ {
155
+ "name": "evolve",
156
+ "display_name": "/codexspec:evolve",
157
+ "description": "将 profile 沉淀编译为命令/技能并通过评审 PR 贡献回上游",
158
+ "category": "enhanced",
159
+ "file_name": "evolve.md",
160
+ },
161
+ {
162
+ "name": "debug",
163
+ "display_name": "/codexspec:debug",
164
+ "description": "系统化根因排查(四阶段:复现→定位根因→单一修复),可独立调用或由 implement-tasks 升级进入",
165
+ "category": "enhanced",
166
+ "file_name": "debug.md",
167
+ },
147
168
  # Git Workflow Commands (2)
148
169
  {
149
170
  "name": "commit-staged",
@@ -10,6 +10,7 @@ from typing import Any
10
10
  import yaml
11
11
 
12
12
  from codexspec.commands.installer import get_commands_metadata
13
+ from codexspec.profile import inject_profile_block
13
14
  from codexspec.translator import load_translation_cache, translate_template_frontmatter
14
15
 
15
16
  CODEXSPEC_CONTEXT_START = "<!-- CODEXSPEC START -->"
@@ -90,6 +91,10 @@ class CodexIntegration:
90
91
 
91
92
  context_path.write_text(updated, encoding="utf-8")
92
93
 
94
+ # Inject the profile block (pointers only, no @import) alongside the
95
+ # skills section, as its own bounded, idempotent managed block.
96
+ inject_profile_block(context_path)
97
+
93
98
  def render_skill(self, command_name: str, content: str, fallback_description: str = "") -> str:
94
99
  """Render one command template into a Codex SKILL.md."""
95
100
  frontmatter, body = _split_frontmatter(content)
@@ -113,6 +118,8 @@ Use these Codex skills when working on CodexSpec workflows:
113
118
  - `$codexspec:spec-to-plan` to produce `plan.md`.
114
119
  - `$codexspec:plan-to-tasks` to produce `tasks.md`.
115
120
  - `$codexspec:implement-tasks` to implement approved tasks.
121
+ - `$codexspec:distill` to capture reusable cross-feature knowledge into `.codexspec/profile/`.
122
+ - `$codexspec:evolve` to contribute vetted profile knowledge back upstream via a reviewed PR.
116
123
 
117
124
  Before making workflow decisions, read `.codexspec/memory/constitution.md`.
118
125
  {CODEXSPEC_CONTEXT_END}
@@ -0,0 +1,112 @@
1
+ """Project-profile consumption.
2
+
3
+ Wires a user project's ``.codexspec/profile/`` into the AI context files so that
4
+ knowledge distilled by ``/codexspec:distill`` is consulted in later work.
5
+
6
+ Store layout: **one record per file** under a per-category directory
7
+ (``constraints/`` ``conventions/`` ``pitfalls/`` ``decisions/``). Because parallel
8
+ feature branches each add differently-named record files, their distilled
9
+ knowledge merges without conflict.
10
+
11
+ Two concerns live here:
12
+
13
+ - ``ensure_profile_scaffold`` — create the four category directories (each kept
14
+ by a ``.gitkeep``) so every injected reference resolves, independent of whether
15
+ any knowledge has been distilled yet.
16
+ - ``render_profile_block`` / ``inject_profile_block`` — render a bounded,
17
+ channel-neutral managed block (pointers only, no ``@import``) and inject it
18
+ idempotently into a context file (CLAUDE.md or AGENTS.md) without disturbing any
19
+ other content.
20
+
21
+ See the feature record under
22
+ ``.codexspec/specs/2026-0812-14054p-profile-consumption/``.
23
+ """
24
+
25
+ import re
26
+ from pathlib import Path
27
+
28
+ # Ordered: constraints first (highest weight, honored first). Each is a directory
29
+ # holding one record per file.
30
+ PROFILE_CATEGORIES = ("constraints", "conventions", "pitfalls", "decisions")
31
+
32
+ PROFILE_BLOCK_START = "<!-- CODEXSPEC PROFILE START -->"
33
+ PROFILE_BLOCK_END = "<!-- CODEXSPEC PROFILE END -->"
34
+
35
+ # Channel-neutral: identical for CLAUDE.md and AGENTS.md. Constraints are a strong
36
+ # mandatory pointer (no @import), so the block depends on no tool-specific import
37
+ # mechanism and the store can be one-file-per-record directories. Assembled from
38
+ # short literals to keep each source line within the line-length limit.
39
+ _PROFILE_BLOCK = "".join(
40
+ [
41
+ f"{PROFILE_BLOCK_START}\n",
42
+ "## CodexSpec Project Profile\n\n",
43
+ "**Project constraints (highest priority — read these FIRST):** before any non-trivial work you MUST ",
44
+ "read every record under `.codexspec/profile/constraints/` — the project's hard prohibitions ",
45
+ "(严禁 / 仅允许). Honor them before anything else.\n\n",
46
+ "**Project profile — consult on demand when relevant to the task** ",
47
+ "(each directory holds one record per file):\n\n",
48
+ "- `.codexspec/profile/conventions/` — cross-feature conventions / steering; ",
49
+ "read before adopting a pattern, structure, or naming choice.\n",
50
+ "- `.codexspec/profile/pitfalls/` — known traps and their workarounds; ",
51
+ "read before implementing or debugging in an area that may have bitten before.\n",
52
+ "- `.codexspec/profile/decisions/` — past cross-feature / architectural decisions; ",
53
+ "read before deciding in the same area, to reuse prior rationale rather than re-litigate it.\n\n",
54
+ "Read the full record — each carries a `status` of `candidate` or `vetted`; ",
55
+ "weight `candidate` items with appropriate caution. A directory may be empty ",
56
+ "until `/codexspec:distill` has captured knowledge.\n",
57
+ f"{PROFILE_BLOCK_END}\n",
58
+ ]
59
+ )
60
+
61
+
62
+ def ensure_profile_scaffold(target_dir: Path) -> Path:
63
+ """Create ``.codexspec/profile/`` and its four category directories if absent.
64
+
65
+ Each category is a directory of one-record-per-file entries; a ``.gitkeep``
66
+ keeps an empty category tracked so every pointer resolves. Idempotent and
67
+ non-destructive: existing records and directories are never overwritten.
68
+ """
69
+ profile_dir = target_dir / ".codexspec" / "profile"
70
+ for category in PROFILE_CATEGORIES:
71
+ category_dir = profile_dir / category
72
+ category_dir.mkdir(parents=True, exist_ok=True)
73
+ keep = category_dir / ".gitkeep"
74
+ if not keep.exists():
75
+ keep.write_text("", encoding="utf-8")
76
+ return profile_dir
77
+
78
+
79
+ def render_profile_block() -> str:
80
+ """Render the bounded managed profile block.
81
+
82
+ Identical for CLAUDE.md and AGENTS.md: constraints and the three on-demand
83
+ categories are all delivered as pointers to their directories — no ``@import``
84
+ anywhere — so the block is channel-neutral and the store can be
85
+ one-file-per-record directories that merge without conflict.
86
+ """
87
+ return _PROFILE_BLOCK
88
+
89
+
90
+ def inject_profile_block(context_path: Path) -> None:
91
+ """Idempotently inject/update the profile block in ``context_path``.
92
+
93
+ Only the bounded ``<!-- CODEXSPEC PROFILE START/END -->`` region is written;
94
+ any other content in the file is preserved verbatim. Creates the file if it
95
+ does not exist.
96
+ """
97
+ block = render_profile_block().rstrip("\n")
98
+ existing = context_path.read_text(encoding="utf-8") if context_path.exists() else ""
99
+
100
+ pattern = re.compile(
101
+ re.escape(PROFILE_BLOCK_START) + r".*?" + re.escape(PROFILE_BLOCK_END),
102
+ re.DOTALL,
103
+ )
104
+ if pattern.search(existing):
105
+ # Function replacement avoids backslash/group interpretation in `block`.
106
+ updated = pattern.sub(lambda _match: block, existing)
107
+ elif existing.strip():
108
+ updated = existing.rstrip() + "\n\n" + block + "\n"
109
+ else:
110
+ updated = block + "\n"
111
+
112
+ context_path.write_text(updated, encoding="utf-8")
@@ -157,3 +157,13 @@ If `git commit` fails due to a pre-commit hook modifying files:
157
157
  - Report the error message to the user
158
158
  - DO NOT attempt to "fix" the situation
159
159
  - The user should investigate and resolve manually
160
+
161
+ ## Automatic Distillation
162
+
163
+ Read `workflow.auto_distill` from `.codexspec/config.yml` (**default `true`** — enabled unless explicitly set to the literal `false`; absent or any non-`false` value means enabled).
164
+
165
+ When `workflow.auto_distill` is enabled (not the literal `false`) AND a commit was created successfully, invoke `/codexspec:distill` exactly once on this session's interaction, then end.
166
+
167
+ - distill is non-blocking and non-interactive: it never prompts and never alters the commit or its message; it early-exits when there is nothing reusable to capture.
168
+ - distill only writes records to `.codexspec/profile/`; it never touches tracked project files or the git state.
169
+ - Do not invoke distill when `auto_distill` is disabled or when no commit was created (for example, an ABORT above).
@@ -107,6 +107,7 @@ Display the configuration as in Step 2, then exit.
107
107
  {"label": "Output language (legacy)", "description": "Fallback language used when interaction/document are not set (currently: {current value})"},
108
108
  {"label": "Commit language", "description": "Language for commit messages (currently: {current value})"},
109
109
  {"label": "Auto-next chain", "description": "Auto-advance the SDD pipeline once a stage passes (workflow.auto_next) (currently: {current value})"},
110
+ {"label": "Auto-distill", "description": "Run /codexspec:distill on completion of wrap-up commands to capture reusable knowledge (workflow.auto_distill) (currently: {current value})"},
110
111
  {"label": "Back", "description": "Return to main menu"}
111
112
  ]
112
113
  }]
@@ -153,6 +154,29 @@ Display the configuration as in Step 2, then exit.
153
154
  a `workflow:` section with `auto_next: <bool>`), preserving every other
154
155
  line and comment.
155
156
 
157
+ 3b. For "Auto-distill", ask whether to enable or disable:
158
+
159
+ ```json
160
+ {
161
+ "questions": [{
162
+ "question": "Set workflow.auto_distill:",
163
+ "header": "Auto-distill",
164
+ "options": [
165
+ {"label": "Enable", "description": "Run /codexspec:distill on completion of wrap-up commands"},
166
+ {"label": "Disable", "description": "Do not auto-distill; run /codexspec:distill manually"},
167
+ {"label": "Back", "description": "Return without changing"}
168
+ ]
169
+ }]
170
+ }
171
+ ```
172
+
173
+ Then read `.codexspec/config.yml`. `auto_distill` is enabled by default; the
174
+ current value is disabled only when `workflow.auto_distill` is the literal
175
+ `false` (an absent key/section, `true`, or any other value is enabled). Write
176
+ `workflow.auto_distill` as an unquoted `true`/`false` (update the value in place
177
+ when the key exists; otherwise add `auto_distill: <bool>` under the `workflow:`
178
+ section, creating that section if absent), preserving every other line and comment.
179
+
156
180
  4. Update the configuration file with the new value
157
181
  5. Display the updated configuration
158
182
  6. Exit
@@ -0,0 +1,80 @@
1
+ ---
2
+ description: Debug a failure to its root cause before proposing any fix
3
+ argument-hint: "[error text | failing test | file:line | plain-language symptom]"
4
+ allowed-tools: Read, Grep, Glob, Bash, Edit, Write
5
+ ---
6
+
7
+ # Systematic Debugger
8
+
9
+ ## Language Preference
10
+
11
+ Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
12
+
13
+ - **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
14
+ - **Document language** (`language.document`): language for generated artifact files (requirements/spec/plan/tasks).
15
+
16
+ Converse in the interaction language and author artifacts in the document language. Apply the project's translation standard to both: translate by meaning (not word-for-word), keep English for terms with no good native equivalent, and write as if originally in that language.
17
+
18
+ ## User Input
19
+
20
+ `$ARGUMENTS`
21
+
22
+ ## Role and Iron Law
23
+
24
+ You debug a reported symptom to its **root cause** and apply exactly one verified fix.
25
+
26
+ **Iron Law: NO FIX BEFORE ROOT CAUSE.** You MUST NOT propose, apply, or even sketch a fix until Phase 1 has established what is actually wrong and why. Symptom patches — wrapping the error, silencing a failing assertion, retrying blindly — are failures, not fixes.
27
+
28
+ Red flags that mean STOP and return to Phase 1: "let me just try changing X", "add a try/except here", "it's probably the Y" — any edit attempted before the failure is reproduced and understood.
29
+
30
+ You leave **no persistent artifact**: no report file, no debug journal. Your output is the fix plus a concise root-cause explanation in the conversation. (Reusable, cross-feature lessons are captured separately by `/codexspec:distill`, never written here.)
31
+
32
+ ## Symptom Intake
33
+
34
+ Take the symptom from `$ARGUMENTS` when provided — an error or stack trace, a failing-test id, a `file:line`, or a plain-language description — otherwise from error output already visible in this session.
35
+
36
+ When the symptom is too thin to act on, **reproduce-or-ask** before doing anything else:
37
+
38
+ - First attempt to reproduce it yourself: run the failing test, exercise the path, read the log or stack trace.
39
+ - If you still cannot reproduce it reliably, ask the user for exactly what is missing — reproduction steps, the precise input that triggers it, expected-vs-actual behavior, the verbatim error, and when it started.
40
+ - Do NOT propose a fix for an unreproduced symptom.
41
+
42
+ ## Investigation Protocol
43
+
44
+ Work the phases in order. Phase 1 is a hard gate.
45
+
46
+ ### Phase 1 — Root-Cause Investigation (hard gate)
47
+
48
+ - Read the error/failure carefully and completely; do not skim.
49
+ - Reproduce it consistently. A flaky or order-dependent failure must be made reliably reproducible before you continue.
50
+ - Check what changed recently — the diff, recent commits, configuration.
51
+ - Trace the data and control flow **backward** from the symptom to where the wrong state originates. Inspect enough callers, callees, and inputs to locate the true origin; it is often not where the error surfaces.
52
+ - **Exit criterion**: you can state, in one sentence, WHAT is wrong and WHY. Until then, no fix.
53
+
54
+ ### Phase 2 — Pattern Analysis
55
+
56
+ - Find a working reference: a passing sibling test, an analogous code path, or an earlier good state.
57
+ - Compare the failing case against it and enumerate every material difference.
58
+ - Identify which difference actually explains the root cause.
59
+
60
+ ### Phase 3 — Hypothesis & Verification
61
+
62
+ - Write down a single, specific hypothesis about the root cause.
63
+ - Test it minimally — change one variable at a time, and predict the outcome before observing it.
64
+ - Confirm or reject. If rejected, reformulate; do not stack untested guesses.
65
+
66
+ ### Phase 4 — Fix
67
+
68
+ - Write a failing test first that captures the defect (a reproducing regression test) and observe it fail for the right reason. For a symptom with no natural unit test — a documentation or configuration defect, a production-log incident — construct the closest reproducing check instead.
69
+ - Apply a single, minimal fix that targets the root cause — not the symptom, and no "while I'm here" changes.
70
+ - Verify: the new test passes and no previously-passing test breaks.
71
+
72
+ ### Architecture Gate (≥3 failed fixes)
73
+
74
+ If three fixes for the same problem have failed, STOP. Do not attempt a fourth blind fix. Repeated failure is evidence that the model of the problem — or the architecture — is wrong. Surface it: state what was tried, why each attempt failed, and what architectural question must be answered before continuing.
75
+
76
+ ## Completion
77
+
78
+ - Report the root cause (one or two sentences), the fix applied, and the verification that shows it green.
79
+ - **When you were entered from another command** (for example, `implement-tasks` escalated into this discipline), do not end the session: hand control back and **resume that command** exactly where it left off, now with the defect resolved.
80
+ - If you could not reach a root cause, or you hit the Architecture Gate, say so plainly with the evidence. Never paper over it with a speculative fix.
@@ -0,0 +1,111 @@
1
+ ---
2
+ description: Distill reusable, cross-feature knowledge from an interaction into the project profile
3
+ argument-hint: "[interaction segment or context to distill]"
4
+ ---
5
+
6
+ # Distill
7
+
8
+ ## Language Preference
9
+
10
+ Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
11
+
12
+ - **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
13
+ - **Document language** (`language.document`): language for generated artifact files (the profile records).
14
+
15
+ Converse in the interaction language and author artifacts in the document language. Apply the project's translation standard to both: translate by meaning (not word-for-word), keep English for terms with no good native equivalent, and write as if originally in that language. **Exception**: `evidence.facts` quotes the user's original words verbatim and MUST NOT be translated.
16
+
17
+ ## User Input
18
+
19
+ `$ARGUMENTS`
20
+
21
+ ## Operating Model
22
+
23
+ `distill` extracts the reusable, cross-feature knowledge produced during work and persists it to the project-level store `.codexspec/profile/`. It runs two ways:
24
+
25
+ - **Auto (primary)**: embedded in wrap-up commands (`implement-tasks` on completion, `commit-staged`, `pr`), gated by `workflow.auto_distill` in `.codexspec/config.yml` (**default enabled**; disabled only when explicitly set to the literal `false`).
26
+ - **Manual (fallback)**: invoked directly on the supplied or most-recent interaction segment.
27
+
28
+ distill is **non-blocking and non-interactive**: it never prompts, never gates another command, and **early-exits without writing** when the delta contains nothing reusable.
29
+
30
+ **Input contract**: distill operates on "a segment of interaction to distill." It MUST NOT assume it is live in the conversation, so the same routine works whether embedded (fed live context) or invoked manually on a supplied segment.
31
+
32
+ ## What distill captures — and what it must NOT
33
+
34
+ Capture **only** knowledge that is reusable **across features** and that the per-feature SDD artifacts structurally cannot accumulate.
35
+
36
+ Apply this boundary test to every candidate: **"Would a single feature's `requirements.md` / `spec.md` / `plan.md` record this?"**
37
+
38
+ - **Yes** → it is feature-scoped; leave it in that artifact. **Do NOT** copy it into the profile. (Requirement rationale already lives in `requirements.md`; approach rationale in plan/design.)
39
+ - **No / it spans features** → it may enter the profile.
40
+
41
+ **Never** create a feature-local store. The profile is project-level only; a feature's memory is its existing spec directory.
42
+
43
+ ## The profile store: `.codexspec/profile/`
44
+
45
+ Four **category directories**, each holding **one record per file** (`<id>.md`) with **only current-effective** knowledge — dense, with no "retired" section (git history is the ledger). One-file-per-record is deliberate: parallel feature branches each add differently-named files, so distilled knowledge merges without conflict. Create the directory and record file on first write.
46
+
47
+ - `constraints/` — negative constraints (`严禁 / 仅允许`). These carry the **highest** weight and MUST be honored first.
48
+ - `conventions/` — positive cross-feature conventions / steering.
49
+ - `pitfalls/` — cross-feature traps and their workarounds.
50
+ - `decisions/` — cross-feature / architectural decisions only (ADR-lite). **Never** single-feature requirement rationale.
51
+
52
+ ### Record format — `claim` and `evidence` physically separated
53
+
54
+ Every record MUST separate the distilled claim from the evidence it rests on:
55
+
56
+ - `id` — **type letter + full source-feature id + local sequence**, e.g. `P-2026-0812-14054p-1` or `Con-2026-0812-14054p-1`. It is **both** the record's `### <id>: <title>` heading **and its filename** (`pitfalls/P-2026-0812-14054p-1.md`). The **source-feature id** is the distilling feature's full spec-dir id `{YYYY-MMDD-HHMM}{rr}` (e.g. `2026-0812-14054p`); it is globally unique by the timestamp+random scheme spec directories use, so records distilled on parallel feature branches never collide on id **or filename** (they merge with no conflict). Keep the **full** id (not a short tail) so the record is self-describing: the date supports recency/staleness reading, and the feature id ties the record to its originating change for decision context and scope. When distilling with no feature context, generate a fresh `{YYYY-MMDD-HHMM}{rr}` id now (same convention as create-new-feature). **Never** use a bare sequential id such as `P-001` — those collide across parallel branches.
57
+ - `claim` — one-sentence reusable statement.
58
+ - `type` — `convention` | `constraint` | `pitfall` | `decision` (`constraint` = highest priority).
59
+ - `scope/when` — natural-language applicability condition (e.g. "when editing Python code"); omit for global. **No formal syntax.**
60
+ - `evidence.facts` — the concrete observations behind it; **quote the user's original words, do not paraphrase**.
61
+ - `evidence.state` — the context/validity when true (feature / commit / config; still valid?).
62
+ - `provenance` — source feature/session, trigger, timestamp, `derivation = explicit | inferred`.
63
+ - `status` — `vetted` **only** when `derivation = explicit` (the user's own words) AND the item was verified by an outcome (a test passed, a workaround worked); every `inferred` item stays `candidate`. Only `vetted` records are eligible for `evolve`.
64
+
65
+ This separation is what makes a later error locatable as **misread** (facts wrong) vs **overreach** (claim over-generalized) vs **stale** (state no longer holds).
66
+
67
+ Example entry — file `conventions/Con-2026-0809-2219gg-1.md`:
68
+
69
+ ```markdown
70
+ ### Con-2026-0809-2219gg-1: Prefer absolute imports
71
+ - claim: Always use absolute imports in `src/`.
72
+ - type: convention
73
+ - scope/when: Python modules under `src/`
74
+ - evidence.facts: "Use absolute imports; relative ones broke the packaged wheel last time."
75
+ - evidence.state: confirmed at feature 2026-0809-2219gg; commit a1b2c3d
76
+ - provenance: distill @implement-tasks, 2026-08-09, derivation: explicit
77
+ - status: vetted
78
+ ```
79
+
80
+ ## Extraction
81
+
82
+ Read the interaction segment and extract, per the dimensions above, only **verified** knowledge — prefer facts confirmed by outcomes over speculation; speculation MUST NOT become `vetted`.
83
+
84
+ Before writing, **read the current profile** (the record files under each category directory) and skip anything already covered; update anything changed via `replace`. **This is how deduplication is done — by judgment, not an algorithm.**
85
+
86
+ ## Conflict adjudication
87
+
88
+ When a new item conflicts with an existing rule, resolve in this order:
89
+
90
+ 1. **Recency** — newer corrections win (usually a `replace`).
91
+ 2. **Specificity** — a specific instruction overrides the general one **only within its scope**.
92
+ 3. **Scenario-decoupling** — if neither wins, keep **both** under a `scope/when` condition rather than forcing a winner.
93
+ 4. **Defer, don't guess** — if genuinely unresolvable, write the record with `status: conflict/needs-adjudication` and surface it at the next interactive point or at evolve time. **Never block, never guess.**
94
+
95
+ ## Mutation discipline
96
+
97
+ Change the profile **only** through three conceptual operations (you edit the files directly — these are a discipline, **not** a tool API or matching algorithm):
98
+
99
+ - `add` — create a new record file `<category>/<id>.md` for a verified item.
100
+ - `replace` — supersede an outdated/wrong item **in its own file** (keeps records dense).
101
+ - `remove` — delete the record's file when a changed environment invalidates it.
102
+
103
+ git history is the audit ledger. Do **NOT** keep a retired file or a retired section.
104
+
105
+ ## Vetting candidates (manual, interactive)
106
+
107
+ Auto-distill writes `candidate` records non-interactively and **never prompts**. Promote them through the **manual review mode** — `/distill review` (or `/distill` with no new segment to distill): list every pending `candidate` compactly (claim + evidence + provenance) and let the user approve inline — "vet all", "vet 1,3", "edit 2", "drop 4". Apply the choices by editing each record's `status` (a `replace`). **The user never hand-edits the profile files.**
108
+
109
+ ## Output
110
+
111
+ Report concisely in the interaction language: which records were added / replaced / removed and in which file, any `conflict` records deferred, or "nothing to distill" on early-exit. distill **never** gates the caller.
@@ -0,0 +1,74 @@
1
+ ---
2
+ description: Compile vetted project-profile knowledge into a reusable command/skill and contribute it upstream via a reviewed PR
3
+ argument-hint: "[what to evolve, or a profile area]"
4
+ ---
5
+
6
+ # Evolve
7
+
8
+ ## Language Preference
9
+
10
+ Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
11
+
12
+ - **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
13
+ - **Document language** (`language.document`): language for generated artifact files.
14
+
15
+ Converse in the interaction language. **The compiled command/skill draft is a distributed template and MUST be authored in English** (project i18n convention), regardless of `language.document`. PR title/body follow `language.commit`.
16
+
17
+ ## User Input
18
+
19
+ `$ARGUMENTS`
20
+
21
+ ## Operating Model
22
+
23
+ `evolve` turns **vetted** sediment in `.codexspec/profile/` into a reusable capability and contributes it back to CodexSpec through a **human-reviewed PR**. It **never merges unattended** and **never** edits install artifacts.
24
+
25
+ ## Selecting what to promote
26
+
27
+ Promote only records that are **both**:
28
+
29
+ 1. `status: vetted` (never `candidate` or `conflict`), and
30
+ 2. **general enough for the toolkit** — the generality extension of distill's boundary test: *"Is this useful to every CodexSpec user, or only to this project?"*
31
+
32
+ Project-specific knowledge **stays** in the profile. Only generally-useful capability is promoted upstream. When nothing qualifies, stop and report — do not force a promotion.
33
+
34
+ ## Compiling the draft
35
+
36
+ Compile the selected sediment into a SKILL.md / command-template draft that conforms to both:
37
+
38
+ - **Anthropic Agent Skills** (SKILL.md + progressive disclosure), and
39
+ - **existing CodexSpec command-template conventions** (YAML frontmatter + sections + `## Language Preference`, English).
40
+
41
+ Apply these compile rules:
42
+
43
+ - **Priority order** — core needs first, **negative constraints immediately after (highest weight)**, then the rest.
44
+ - **Logic-clean** — `replace`/`remove` any superseded rule first; the output MUST carry **no** contradictory rules.
45
+ - **Imperative wording** — use **必须 / 始终 / 严禁 / 仅允许** in place of 可以考虑 / 尽量 / 最好不要 / 或许. Match the project's explicit **Prefer / Avoid** rule style; do not use decorative markers.
46
+
47
+ ## Where output goes (self-bootstrap)
48
+
49
+ Write **only** under `templates/` — a new `templates/commands/*.md` or a standalone skill package. **NEVER** edit `.claude/commands/codexspec/`: it is a regenerated install artifact, and any edit there is silently overwritten on the next reinstall and never reaches users. Changes reach users via `publish` → `init`.
50
+
51
+ ## Contribution mechanics
52
+
53
+ **Before any `git push` or PR creation, present the compiled draft file(s) and the value statement to the user and obtain explicit approval. Proceed only on approval; NEVER push or open a PR unattended.** (Writing a local draft under `templates/` is git-reversible; the outward action is what is gated.)
54
+
55
+ On approval, open a PR for human review. **Auto-detect** the git path — this is a mechanics difference only, never a permission tier:
56
+
57
+ - Upstream **write access** → push a branch in-repo and open the PR.
58
+ - **No write access** → fork, push to the fork, open a cross-repo PR.
59
+
60
+ Both take the **identical** review path.
61
+
62
+ ## Value gate and PR summary
63
+
64
+ Produce a one-sentence **value statement** as the PR summary:
65
+
66
+ > Resolves `<pain>`, by `<added/revised constraint>`, achieving `<quality/efficiency gain>`.
67
+
68
+ If no crisp value statement can be written, **open NO PR** — the batch is not worth promoting (this is the lightweight substitute for a metric/eval gate).
69
+
70
+ For review, keep each promoted `claim` paired with its `evidence` so a reviewer can check **claim ⇐ evidence**. A promoted change that later proves worse MUST be rolled back via `remove`/`replace` (git-traceable), not a manual file edit.
71
+
72
+ ## Output
73
+
74
+ Report in the interaction language: what was selected, the draft file(s) written under `templates/`, and the PR (branch or fork) with its value statement — or **"nothing promoted"** with the reason (value gate / nothing vetted / nothing general enough).
@@ -90,6 +90,7 @@ For **each task**, determine the workflow based on task type:
90
90
  3. **Verify - Run Tests**
91
91
  - Execute all relevant tests
92
92
  - Ensure new tests pass and no existing tests break
93
+ - If a test stays red across several green attempts, a fix reddens a previously-passing test, or you catch yourself guessing: stop patching and follow **Systematic Debugging Escalation** (below)
93
94
 
94
95
  4. **Review & Refactor**
95
96
  - Check for bugs, edge cases, security issues
@@ -231,7 +232,9 @@ Apply only verified repairs:
231
232
 
232
233
  - For a functional defect, first add a reproducing regression test and observe
233
234
  the expected failure. Then use red-green-refactor until the defect is fixed
234
- while existing behavior remains green.
235
+ while existing behavior remains green. When such a repair is non-trivial — the
236
+ cause is not a mechanical local edit but must be traced across call chains,
237
+ state, or data flow — follow **Systematic Debugging Escalation** (below).
235
238
  - For documentation and non-code configuration defects, use the applicable
236
239
  deterministic checks before and after the repair. Do not manufacture a code
237
240
  test when the binding contract is non-code.
@@ -292,3 +295,32 @@ or a commit.
292
295
  - Commits remain outside verdict logic. If the surrounding workflow calls for
293
296
  a commit, create it only after the applicable checks are green; a commit must
294
297
  never alter, replace, or imply the review verdict.
298
+
299
+ ## Systematic Debugging Escalation
300
+
301
+ When a fix is not converging, escalate into the systematic root-cause discipline instead of continuing to patch. This is a reference, not a duplicate: the discipline lives once in `/codexspec:debug`.
302
+
303
+ **Trip conditions** (either one):
304
+
305
+ - **(a) During the TDD Verify/green loop (§3)**: the same test stays red after several green attempts, a fix reddens a previously-passing test, or you notice guess-and-check behavior.
306
+ - **(b) During a test-safe repair (§7.4)**: you are fixing a **functional/correctness (or robustness) defect** whose fix is **non-trivial** — it requires tracing across call chains, state, or data flow, not a mechanical local edit. This trip does NOT apply to idiomatic-clarity, architecture, constitution-alignment, style, or trivial mechanical fixes.
307
+
308
+ **Escalation**:
309
+
310
+ ```text
311
+ Invoke /codexspec:debug
312
+ ```
313
+
314
+ Apply its root-cause discipline to the failing test (trip a) or the defect under repair (trip b). The escalation is **non-gating and low-ceremony**: it produces no PASS/FAIL, emits no mandatory notice line, and does not interrupt the user.
315
+
316
+ **Resume**: once `debug` has reached the root cause and applied a verified fix, **return here and continue** the task or repair exactly where you left off — re-establish the green baseline and proceed. There is no runtime stack; resuming is your responsibility, not the engine's.
317
+
318
+ ## Automatic Distillation
319
+
320
+ Read `workflow.auto_distill` from `.codexspec/config.yml` (**default `true`** — enabled unless explicitly set to the literal `false`; absent or any non-`false` value means enabled).
321
+
322
+ When `workflow.auto_distill` is enabled (not the literal `false`) AND this command reported success (§7.6), invoke `/codexspec:distill` exactly once on this session's interaction, then end.
323
+
324
+ - distill is non-blocking and non-interactive: it never prompts, never changes this command's verdict or report, and early-exits when there is nothing reusable to capture.
325
+ - distill only writes `candidate`/`vetted` records to `.codexspec/profile/`; it MUST NOT modify `requirements.md`, `spec.md`, `plan.md`, or `tasks.md`.
326
+ - Do not invoke distill when `auto_distill` is disabled or when this command did not report success.
@@ -622,3 +622,13 @@ When saving to a file, output the raw markdown content directly (without code bl
622
622
  - Include enough detail for reviewers to understand the changes
623
623
  - Do not include any AI attribution in the PR description
624
624
  - Focus on clarity and usefulness for code reviewers
625
+
626
+ ## Automatic Distillation
627
+
628
+ Read `workflow.auto_distill` from `.codexspec/config.yml` (**default `true`** — enabled unless explicitly set to the literal `false`; absent or any non-`false` value means enabled).
629
+
630
+ When `workflow.auto_distill` is enabled (not the literal `false`) AND a PR/MR description was generated successfully, invoke `/codexspec:distill` exactly once on this session's interaction, then end.
631
+
632
+ - distill is non-blocking and non-interactive: it never prompts and never alters the generated description; it early-exits when there is nothing reusable to capture.
633
+ - distill only writes records to `.codexspec/profile/`.
634
+ - Do not invoke distill when `auto_distill` is disabled or when no description was produced.
@@ -50,6 +50,15 @@ When the argument identifies an existing feature:
50
50
  4. Load the existing `requirements.md`.
51
51
  5. Legacy feature: if only `spec.md` exists, extract candidate entries from it, mark them `open`, and require user confirmation before they become authoritative.
52
52
 
53
+ ## Consult Project Profile
54
+
55
+ Before discussing and finalizing requirements, read the project profile under `.codexspec/profile/` when it exists so the confirmed `requirements.md` is a synthesis that already accounts for accumulated project knowledge. Each category is a directory holding one record per file:
56
+
57
+ - `constraints/` first — the project's hard prohibitions (highest weight); requirements MUST NOT contradict them.
58
+ - `pitfalls/`, `conventions/`, `decisions/` — read the records relevant to this feature's area, to avoid re-hitting known traps, to follow established conventions, and to reuse past cross-feature/architectural decisions rather than re-litigating them.
59
+
60
+ Each record carries a `status` (`candidate` or `vetted`); weight `candidate` entries with appropriate caution. Fold what is relevant into the discussion and the resulting entries; cite a profile record as evidence when it materially shapes a decision. This is the single point where the profile enters the SDD pipeline — downstream stages keep `requirements.md` as authority and do not re-read the profile. Degrade silently when the profile is absent or empty (nothing to apply); never block on it.
61
+
53
62
  ## Discussion Rules
54
63
 
55
64
  - Ask one material question at a time.
File without changes
File without changes
File without changes