codexspec 0.7.6__tar.gz → 0.7.7__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.
- {codexspec-0.7.6 → codexspec-0.7.7}/PKG-INFO +9 -1
- {codexspec-0.7.6 → codexspec-0.7.7}/README.md +8 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/pyproject.toml +1 -1
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/__init__.py +150 -10
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/commands/installer.py +16 -2
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/integrations/codex.py +2 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/commit-staged.md +10 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/config.md +24 -0
- codexspec-0.7.7/templates/commands/distill.md +110 -0
- codexspec-0.7.7/templates/commands/evolve.md +74 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/implement-tasks.md +10 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/pr.md +10 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/.gitignore +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/LICENSE +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/codexspec-icon.svg +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/codexspec-logo-dark.svg +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/codexspec-logo-light.svg +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/check-i18n-completeness.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/check-i18n-structure.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/check-prerequisites.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/common.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/create-new-feature.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/bash/review-context.sh +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/powershell/common.ps1 +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/powershell/create-new-feature.ps1 +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/scripts/powershell/review-context.ps1 +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/commands/__init__.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/i18n.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/idea.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/integrations/__init__.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/integrations/base.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/integrations/claude.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/src/codexspec/translator.py +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/analyze.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/checklist.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/clarify.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/constitution.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/generate-spec.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/plan-to-tasks.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/quick.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/review-code.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/review-plan.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/review-spec.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/review-tasks.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/spec-to-plan.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/specify.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/commands/tasks-to-issues.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/checklist-template.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/constitution-template.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/plan-template-detailed.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/plan-template-simple.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/requirements-template.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/spec-template-detailed.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/spec-template-simple.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/tasks-template-detailed.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/docs/tasks-template-simple.md +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/de.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/en.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/es.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/fr.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/ja.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/ko.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/pt-BR.json +0 -0
- {codexspec-0.7.6 → codexspec-0.7.7}/templates/translations/zh-CN.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codexspec
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.7
|
|
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
|
|
|
@@ -589,6 +590,13 @@ Implementation follows **conditional TDD workflow**:
|
|
|
589
590
|
| `/codexspec:checklist` | Generate requirements quality checklist |
|
|
590
591
|
| `/codexspec:tasks-to-issues` | Convert tasks to GitHub Issues |
|
|
591
592
|
|
|
593
|
+
#### Self-Evolution Commands
|
|
594
|
+
|
|
595
|
+
| Command | Description |
|
|
596
|
+
| --- | --- |
|
|
597
|
+
| `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
|
|
598
|
+
| `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
|
|
599
|
+
|
|
592
600
|
#### Git Workflow Commands
|
|
593
601
|
|
|
594
602
|
| Command | Description |
|
|
@@ -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
|
|
|
@@ -544,6 +545,13 @@ Implementation follows **conditional TDD workflow**:
|
|
|
544
545
|
| `/codexspec:checklist` | Generate requirements quality checklist |
|
|
545
546
|
| `/codexspec:tasks-to-issues` | Convert tasks to GitHub Issues |
|
|
546
547
|
|
|
548
|
+
#### Self-Evolution Commands
|
|
549
|
+
|
|
550
|
+
| Command | Description |
|
|
551
|
+
| --- | --- |
|
|
552
|
+
| `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
|
|
553
|
+
| `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
|
|
554
|
+
|
|
547
555
|
#### Git Workflow Commands
|
|
548
556
|
|
|
549
557
|
| Command | Description |
|
|
@@ -43,7 +43,7 @@ from .integrations import get_integrations
|
|
|
43
43
|
from .translator import SUPPORTED_LANGUAGES, translate
|
|
44
44
|
|
|
45
45
|
# Version info
|
|
46
|
-
__version__ = "0.7.
|
|
46
|
+
__version__ = "0.7.7"
|
|
47
47
|
__author__ = "CodexSpec Team"
|
|
48
48
|
|
|
49
49
|
# Constitution file path constants
|
|
@@ -181,6 +181,11 @@ _AUTO_NEXT_FALSE = {"off", "false", "0", "no"}
|
|
|
181
181
|
_AUTO_NEXT_SENTINEL = "__toggle__"
|
|
182
182
|
_AUTO_NEXT_ACCEPTED = "on/off, true/false, 1/0, yes/no"
|
|
183
183
|
|
|
184
|
+
# workflow.auto_distill toggle. Unlike auto_next, auto_distill defaults to ON
|
|
185
|
+
# (opt-out): only the literal ``false`` disables it. Reuses the auto_next token
|
|
186
|
+
# sets for parsing; a distinct sentinel keeps the bare-toggle rewrites separate.
|
|
187
|
+
_AUTO_DISTILL_SENTINEL = "__toggle_distill__"
|
|
188
|
+
|
|
184
189
|
|
|
185
190
|
@app.command()
|
|
186
191
|
def config(
|
|
@@ -221,6 +226,13 @@ def config(
|
|
|
221
226
|
"--auto-next",
|
|
222
227
|
help="Toggle workflow.auto_next (bare), or set it (on/off|true/false|1/0|yes/no).",
|
|
223
228
|
),
|
|
229
|
+
# ``--auto-distill`` mirrors ``--auto-next``; the bare toggle is rewritten to
|
|
230
|
+
# ``--auto-distill=<sentinel>`` by ``_normalize_auto_distill_argv`` in ``main()``.
|
|
231
|
+
auto_distill: Optional[str] = typer.Option(
|
|
232
|
+
None,
|
|
233
|
+
"--auto-distill",
|
|
234
|
+
help="Toggle workflow.auto_distill (bare), or set it (on/off|true/false|1/0|yes/no).",
|
|
235
|
+
),
|
|
224
236
|
) -> None:
|
|
225
237
|
"""
|
|
226
238
|
View or modify CodexSpec project configuration.
|
|
@@ -233,6 +245,7 @@ def config(
|
|
|
233
245
|
codexspec config --set-lang zh-CN # Set language to Chinese
|
|
234
246
|
codexspec config --set-commit-lang en # Set commit messages to English
|
|
235
247
|
codexspec config --auto-next # Toggle workflow.auto_next
|
|
248
|
+
codexspec config --auto-distill off # Disable workflow.auto_distill (default on)
|
|
236
249
|
codexspec config --list-langs # List supported languages
|
|
237
250
|
"""
|
|
238
251
|
# Handle list languages
|
|
@@ -274,6 +287,25 @@ def config(
|
|
|
274
287
|
raise typer.Exit(1)
|
|
275
288
|
return
|
|
276
289
|
|
|
290
|
+
# Handle auto-distill toggle/set
|
|
291
|
+
if auto_distill is not None:
|
|
292
|
+
if auto_distill == _AUTO_DISTILL_SENTINEL:
|
|
293
|
+
target = not _read_auto_distill(config_file)
|
|
294
|
+
else:
|
|
295
|
+
try:
|
|
296
|
+
target = parse_auto_distill_value(auto_distill)
|
|
297
|
+
except ValueError:
|
|
298
|
+
console.print(f"[red]Invalid --auto-distill value:[/red] {auto_distill!r}")
|
|
299
|
+
console.print(f"Accepted values: {_AUTO_NEXT_ACCEPTED} (or pass --auto-distill bare to toggle).")
|
|
300
|
+
raise typer.Exit(1)
|
|
301
|
+
if _write_auto_distill(config_file, target):
|
|
302
|
+
state = "enabled" if target else "disabled"
|
|
303
|
+
console.print(f"[green]auto_distill {state}[/green] (workflow.auto_distill = {str(target).lower()})")
|
|
304
|
+
else:
|
|
305
|
+
console.print("[red]Failed to update workflow.auto_distill[/red]")
|
|
306
|
+
raise typer.Exit(1)
|
|
307
|
+
return
|
|
308
|
+
|
|
277
309
|
# Handle set language
|
|
278
310
|
if set_lang:
|
|
279
311
|
normalized = normalize_locale(set_lang)
|
|
@@ -1042,6 +1074,101 @@ def _dump_lines(path: Path, lines: list[str]) -> bool:
|
|
|
1042
1074
|
return True
|
|
1043
1075
|
|
|
1044
1076
|
|
|
1077
|
+
# --- workflow.auto_distill toggle helpers ----------------------------------
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
def parse_auto_distill_value(raw: str) -> bool:
|
|
1081
|
+
"""Parse an explicit ``--auto-distill`` value into a boolean.
|
|
1082
|
+
|
|
1083
|
+
Accepts the same tokens as ``--auto-next`` (``on/off``, ``true/false``,
|
|
1084
|
+
``1/0``, ``yes/no``; case-insensitive, surrounding whitespace ignored).
|
|
1085
|
+
Raises ``ValueError`` for any other token.
|
|
1086
|
+
"""
|
|
1087
|
+
token = (raw or "").strip().lower()
|
|
1088
|
+
if token in _AUTO_NEXT_TRUE:
|
|
1089
|
+
return True
|
|
1090
|
+
if token in _AUTO_NEXT_FALSE:
|
|
1091
|
+
return False
|
|
1092
|
+
raise ValueError(f"invalid --auto-distill value: {raw!r}")
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _read_auto_distill(config_file: Path) -> bool:
|
|
1096
|
+
"""Return True unless ``workflow.auto_distill`` is the literal ``false``.
|
|
1097
|
+
|
|
1098
|
+
``auto_distill`` defaults to enabled (opt-out): an absent key/section, an
|
|
1099
|
+
explicit ``true``, or any non-``false`` value enables it; only the literal
|
|
1100
|
+
``false`` disables it. A missing file also reads as enabled.
|
|
1101
|
+
"""
|
|
1102
|
+
try:
|
|
1103
|
+
content = config_file.read_text(encoding="utf-8")
|
|
1104
|
+
except OSError:
|
|
1105
|
+
return True
|
|
1106
|
+
in_workflow = False
|
|
1107
|
+
for line in content.splitlines():
|
|
1108
|
+
if not line.strip():
|
|
1109
|
+
continue
|
|
1110
|
+
if not line[0].isspace(): # top-level key (or comment)
|
|
1111
|
+
key = line.split("#", 1)[0].strip()
|
|
1112
|
+
in_workflow = key == "workflow:"
|
|
1113
|
+
continue
|
|
1114
|
+
if in_workflow:
|
|
1115
|
+
match = re.match(r"^\s*auto_distill:\s*(\S+?)\s*(?:#.*)?$", line)
|
|
1116
|
+
if match:
|
|
1117
|
+
return match.group(1) != "false"
|
|
1118
|
+
return True
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def _write_auto_distill(config_file: Path, value: bool) -> bool:
|
|
1122
|
+
"""Set ``workflow.auto_distill`` to an unquoted boolean.
|
|
1123
|
+
|
|
1124
|
+
Mirrors ``_write_auto_next``: (1) update the value in place when the key
|
|
1125
|
+
exists under ``workflow:``; (2) insert it as the section's first child when
|
|
1126
|
+
the section exists but the key is absent; (3) append a ``workflow:`` section
|
|
1127
|
+
when absent. Preserves all other lines/comments. Returns ``False`` on I/O
|
|
1128
|
+
error.
|
|
1129
|
+
"""
|
|
1130
|
+
try:
|
|
1131
|
+
content = config_file.read_text(encoding="utf-8")
|
|
1132
|
+
except OSError:
|
|
1133
|
+
return False
|
|
1134
|
+
|
|
1135
|
+
token = "true" if value else "false"
|
|
1136
|
+
lines = content.split("\n")
|
|
1137
|
+
|
|
1138
|
+
workflow_idx: Optional[int] = None
|
|
1139
|
+
in_workflow = False
|
|
1140
|
+
for i, line in enumerate(lines):
|
|
1141
|
+
if not line.strip():
|
|
1142
|
+
continue
|
|
1143
|
+
if not line[0].isspace():
|
|
1144
|
+
key = line.split("#", 1)[0].strip()
|
|
1145
|
+
in_workflow = key == "workflow:"
|
|
1146
|
+
if in_workflow:
|
|
1147
|
+
workflow_idx = i
|
|
1148
|
+
continue
|
|
1149
|
+
if in_workflow and re.match(r"^\s*auto_distill:\s*\S+", line):
|
|
1150
|
+
indent = line[: len(line) - len(line.lstrip())]
|
|
1151
|
+
lines[i] = f"{indent}auto_distill: {token}"
|
|
1152
|
+
return _dump_lines(config_file, lines)
|
|
1153
|
+
|
|
1154
|
+
if workflow_idx is not None:
|
|
1155
|
+
lines.insert(workflow_idx + 1, f" auto_distill: {token}")
|
|
1156
|
+
return _dump_lines(config_file, lines)
|
|
1157
|
+
|
|
1158
|
+
section = f"workflow:\n auto_distill: {token}"
|
|
1159
|
+
if not content:
|
|
1160
|
+
new_content = section + "\n"
|
|
1161
|
+
elif content.endswith("\n"):
|
|
1162
|
+
new_content = content + "\n" + section + "\n"
|
|
1163
|
+
else:
|
|
1164
|
+
new_content = content + "\n\n" + section + "\n"
|
|
1165
|
+
try:
|
|
1166
|
+
config_file.write_text(new_content, encoding="utf-8")
|
|
1167
|
+
except OSError:
|
|
1168
|
+
return False
|
|
1169
|
+
return True
|
|
1170
|
+
|
|
1171
|
+
|
|
1045
1172
|
def _next_step_start(integration_keys: set[str], language: str) -> str:
|
|
1046
1173
|
"""Return a target-aware start instruction."""
|
|
1047
1174
|
if integration_keys == {"codex"}:
|
|
@@ -1439,24 +1566,23 @@ The following slash commands are available in this project:
|
|
|
1439
1566
|
"""
|
|
1440
1567
|
|
|
1441
1568
|
|
|
1442
|
-
def
|
|
1443
|
-
"""Rewrite a bare
|
|
1569
|
+
def _normalize_optional_value_argv(argv: list[str], flag: str, sentinel: str) -> list[str]:
|
|
1570
|
+
"""Rewrite a bare ``flag`` into ``flag=<sentinel>``.
|
|
1444
1571
|
|
|
1445
1572
|
Click 8.3 no longer honors ``flag_value`` for a bare optional-value option
|
|
1446
|
-
(it errors "requires an argument").
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
``config`` handler interprets as a toggle.
|
|
1573
|
+
(it errors "requires an argument"). A standalone ``flag`` token — one that is
|
|
1574
|
+
not already in ``flag=...`` form and is not followed by a value token — is
|
|
1575
|
+
rewritten to ``flag=<sentinel>``, which the ``config`` handler reads as a
|
|
1576
|
+
toggle.
|
|
1451
1577
|
"""
|
|
1452
1578
|
out: list[str] = []
|
|
1453
1579
|
i = 0
|
|
1454
1580
|
while i < len(argv):
|
|
1455
1581
|
tok = argv[i]
|
|
1456
|
-
if tok ==
|
|
1582
|
+
if tok == flag:
|
|
1457
1583
|
nxt = argv[i + 1] if i + 1 < len(argv) else None
|
|
1458
1584
|
if nxt is None or nxt.startswith("-"):
|
|
1459
|
-
out.append(f"
|
|
1585
|
+
out.append(f"{flag}={sentinel}")
|
|
1460
1586
|
else:
|
|
1461
1587
|
out.append(tok)
|
|
1462
1588
|
out.append(nxt)
|
|
@@ -1467,10 +1593,24 @@ def _normalize_auto_next_argv(argv: list[str]) -> list[str]:
|
|
|
1467
1593
|
return out
|
|
1468
1594
|
|
|
1469
1595
|
|
|
1596
|
+
def _normalize_auto_next_argv(argv: list[str]) -> list[str]:
|
|
1597
|
+
"""Rewrite a bare ``--auto-next`` into ``--auto-next=<sentinel>`` (see
|
|
1598
|
+
``_normalize_optional_value_argv``)."""
|
|
1599
|
+
return _normalize_optional_value_argv(argv, "--auto-next", _AUTO_NEXT_SENTINEL)
|
|
1600
|
+
|
|
1601
|
+
|
|
1602
|
+
def _normalize_auto_distill_argv(argv: list[str]) -> list[str]:
|
|
1603
|
+
"""Rewrite a bare ``--auto-distill`` into ``--auto-distill=<sentinel>`` (see
|
|
1604
|
+
``_normalize_optional_value_argv``)."""
|
|
1605
|
+
return _normalize_optional_value_argv(argv, "--auto-distill", _AUTO_DISTILL_SENTINEL)
|
|
1606
|
+
|
|
1607
|
+
|
|
1470
1608
|
def main() -> None:
|
|
1471
1609
|
"""Main entry point for the CLI."""
|
|
1472
1610
|
if "--auto-next" in sys.argv:
|
|
1473
1611
|
sys.argv = _normalize_auto_next_argv(sys.argv)
|
|
1612
|
+
if "--auto-distill" in sys.argv:
|
|
1613
|
+
sys.argv = _normalize_auto_distill_argv(sys.argv)
|
|
1474
1614
|
app()
|
|
1475
1615
|
|
|
1476
1616
|
|
|
@@ -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 (
|
|
51
|
-
Total:
|
|
50
|
+
core (9) -> enhanced (6) -> git (2) -> review (1) -> utility (2)
|
|
51
|
+
Total: 20 commands
|
|
52
52
|
"""
|
|
53
53
|
return [
|
|
54
54
|
# Core Commands (9)
|
|
@@ -144,6 +144,20 @@ 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
|
+
},
|
|
147
161
|
# Git Workflow Commands (2)
|
|
148
162
|
{
|
|
149
163
|
"name": "commit-staged",
|
|
@@ -113,6 +113,8 @@ Use these Codex skills when working on CodexSpec workflows:
|
|
|
113
113
|
- `$codexspec:spec-to-plan` to produce `plan.md`.
|
|
114
114
|
- `$codexspec:plan-to-tasks` to produce `tasks.md`.
|
|
115
115
|
- `$codexspec:implement-tasks` to implement approved tasks.
|
|
116
|
+
- `$codexspec:distill` to capture reusable cross-feature knowledge into `.codexspec/profile/`.
|
|
117
|
+
- `$codexspec:evolve` to contribute vetted profile knowledge back upstream via a reviewed PR.
|
|
116
118
|
|
|
117
119
|
Before making workflow decisions, read `.codexspec/memory/constitution.md`.
|
|
118
120
|
{CODEXSPEC_CONTEXT_END}
|
|
@@ -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,110 @@
|
|
|
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 markdown files, each holding **only current-effective** knowledge — dense, with no "retired" section (git history is the ledger). Create the directory and file on first write.
|
|
46
|
+
|
|
47
|
+
- `constraints.md` — negative constraints (`严禁 / 仅允许`). These carry the **highest** weight and MUST be honored first.
|
|
48
|
+
- `conventions.md` — positive cross-feature conventions / steering.
|
|
49
|
+
- `pitfalls.md` — cross-feature traps and their workarounds.
|
|
50
|
+
- `decisions.md` — 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
|
+
- `claim` — one-sentence reusable statement.
|
|
57
|
+
- `type` — `convention` | `constraint` | `pitfall` | `decision` (`constraint` = highest priority).
|
|
58
|
+
- `scope/when` — natural-language applicability condition (e.g. "when editing Python code"); omit for global. **No formal syntax.**
|
|
59
|
+
- `evidence.facts` — the concrete observations behind it; **quote the user's original words, do not paraphrase**.
|
|
60
|
+
- `evidence.state` — the context/validity when true (feature / commit / config; still valid?).
|
|
61
|
+
- `provenance` — source feature/session, trigger, timestamp, `derivation = explicit | inferred`.
|
|
62
|
+
- `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`.
|
|
63
|
+
|
|
64
|
+
This separation is what makes a later error locatable as **misread** (facts wrong) vs **overreach** (claim over-generalized) vs **stale** (state no longer holds).
|
|
65
|
+
|
|
66
|
+
Example entry:
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
### C-003: Prefer absolute imports
|
|
70
|
+
- claim: Always use absolute imports in `src/`.
|
|
71
|
+
- type: convention
|
|
72
|
+
- scope/when: Python modules under `src/`
|
|
73
|
+
- evidence.facts: "Use absolute imports; relative ones broke the packaged wheel last time."
|
|
74
|
+
- evidence.state: confirmed at feature 2026-0809-2219gg; commit a1b2c3d
|
|
75
|
+
- provenance: distill @implement-tasks, 2026-08-09, derivation: explicit
|
|
76
|
+
- status: vetted
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Extraction
|
|
80
|
+
|
|
81
|
+
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`.
|
|
82
|
+
|
|
83
|
+
Before writing, **read the current profile** and skip anything already covered; update anything changed via `replace`. **This is how deduplication is done — by judgment, not an algorithm.**
|
|
84
|
+
|
|
85
|
+
## Conflict adjudication
|
|
86
|
+
|
|
87
|
+
When a new item conflicts with an existing rule, resolve in this order:
|
|
88
|
+
|
|
89
|
+
1. **Recency** — newer corrections win (usually a `replace`).
|
|
90
|
+
2. **Specificity** — a specific instruction overrides the general one **only within its scope**.
|
|
91
|
+
3. **Scenario-decoupling** — if neither wins, keep **both** under a `scope/when` condition rather than forcing a winner.
|
|
92
|
+
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.**
|
|
93
|
+
|
|
94
|
+
## Mutation discipline
|
|
95
|
+
|
|
96
|
+
Change the profile **only** through three conceptual operations (you edit the markdown directly — these are a discipline, **not** a tool API or matching algorithm):
|
|
97
|
+
|
|
98
|
+
- `add` — append a new verified item.
|
|
99
|
+
- `replace` — supersede an outdated/wrong item in place (keeps files dense).
|
|
100
|
+
- `remove` — delete an item invalidated by a changed environment.
|
|
101
|
+
|
|
102
|
+
git history is the audit ledger. Do **NOT** keep a retired section inside the files.
|
|
103
|
+
|
|
104
|
+
## Vetting candidates (manual, interactive)
|
|
105
|
+
|
|
106
|
+
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.**
|
|
107
|
+
|
|
108
|
+
## Output
|
|
109
|
+
|
|
110
|
+
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).
|
|
@@ -292,3 +292,13 @@ or a commit.
|
|
|
292
292
|
- Commits remain outside verdict logic. If the surrounding workflow calls for
|
|
293
293
|
a commit, create it only after the applicable checks are green; a commit must
|
|
294
294
|
never alter, replace, or imply the review verdict.
|
|
295
|
+
|
|
296
|
+
## Automatic Distillation
|
|
297
|
+
|
|
298
|
+
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).
|
|
299
|
+
|
|
300
|
+
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.
|
|
301
|
+
|
|
302
|
+
- 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.
|
|
303
|
+
- distill only writes `candidate`/`vetted` records to `.codexspec/profile/`; it MUST NOT modify `requirements.md`, `spec.md`, `plan.md`, or `tasks.md`.
|
|
304
|
+
- 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.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|