guild-cli 0.26.22__tar.gz → 0.26.23__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.
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/version-bump/SKILL.md +15 -3
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/version-bump/scripts/bump.py +117 -23
- {guild_cli-0.26.22 → guild_cli-0.26.23}/CHANGELOG.md +11 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/PKG-INFO +1 -1
- {guild_cli-0.26.22 → guild_cli-0.26.23}/pyproject.toml +1 -1
- guild_cli-0.26.23/tests/test_version_bump_script.py +224 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/uv.lock +1 -1
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/agent-config/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/prompts/review.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/prompts/write.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/challenge/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/challenge/scripts/challenge.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/deviate/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/deviate/scripts/deviate.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/guild/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/guild/scripts/configure-repo.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/guild/scripts/create.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/guild/scripts/overview.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/onboard/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/onboard/scripts/onboard.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/outsource/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/outsource/prompts/explore.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/outsource/prompts/review.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/outsource/prompts/write.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/outsource/scripts/outsource.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/recall/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/recall/scripts/recall.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/remember/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/remember/scripts/remember.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/run-tests/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/scope/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/scope/scripts/scope.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/summarize-delivery/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/summarize-delivery/scripts/summarize-delivery.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/teach/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/teach/scripts/teach.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/think/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/think/scripts/think.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/validate-delivery/SKILL.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/validate-delivery/scripts/validate-delivery.sh +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills.local.yaml.example +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/current +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/current_plan +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/deliveries/template-supports-four-harnesses.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/frames/guildmaster-ships-teach-and-onboard-two-agent-firs.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/frames/template-supports-four-harnesses.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/plans/guildmaster-ships-teach-and-onboard-two-agent-firs.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/plans/template-supports-four-harnesses.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.eidetic/memory/culture-agent-template__public.jsonl +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.eidetic/memory/default__public.jsonl +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.eidetic/memory/guildmaster__public.jsonl +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.flake8 +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.github/workflows/publish.yml +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.github/workflows/tests.yml +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.gitignore +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/.markdownlint-cli2.yaml +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/AGENTS.override.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/CLAUDE.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/LICENSE +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/QWEN.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/README.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/culture.yaml +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/cutover.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/deliveries/2026-09-05-template-supports-four-harnesses.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/onboarding/reachy-mini-mcp.json +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/plans/2026-05-24-guildmaster-ships-teach-and-onboard-two-agent-firs.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/plans/2026-09-05-template-supports-four-harnesses-split.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/plans/2026-09-05-template-supports-four-harnesses.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/skill-sources.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/specs/2026-05-24-guildmaster-ships-teach-and-onboard-two-agent-firs.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/docs/specs/2026-09-05-template-supports-four-harnesses.md +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/__main__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/_broadcast.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/_provision_template.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/create.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/explain.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/harness.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/learn.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/onboard.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/overview.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/show.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/teach.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_commands/whoami.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_errors.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_output.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/cli/_repo.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/scaffold/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/scaffold/harness.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/scaffold/harness_use.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/scaffold/instantiate.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/skills/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/skills/identity.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/skills/ledger.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/skills/render.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/guild/skills/sources.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/sonar-project.properties +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/__init__.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_broadcast_post.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_create.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_explain.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_harness.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_learn.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_onboard.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_overview.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_show.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_teach.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_cli_whoami.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_configure_repo_sonar.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_create_gate.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_scaffold_harness.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_scaffold_harness_use.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_scaffold_instantiate.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_skills_convention.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_skills_identity.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_skills_ledger.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_skills_render.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_skills_sources.py +0 -0
- {guild_cli-0.26.22 → guild_cli-0.26.23}/tests/test_version_fallback.py +0 -0
|
@@ -24,8 +24,10 @@ Run from the repo root.
|
|
|
24
24
|
echo '{"added":["New X"],"changed":["Refactored Y"],"fixed":["Bug in Z"]}' \
|
|
25
25
|
| python3 .claude/skills/version-bump/scripts/bump.py minor
|
|
26
26
|
|
|
27
|
-
# Without changelog content (inserts empty ### Added/Changed/Fixed stubs)
|
|
28
|
-
|
|
27
|
+
# Without changelog content (inserts empty ### Added/Changed/Fixed stubs).
|
|
28
|
+
# From scripts, daemons and agent harnesses, pass --no-stdin so the bump
|
|
29
|
+
# never waits on a stdin it was handed but nobody writes to:
|
|
30
|
+
python3 .claude/skills/version-bump/scripts/bump.py patch --no-stdin
|
|
29
31
|
|
|
30
32
|
# Check current version without bumping:
|
|
31
33
|
python3 .claude/skills/version-bump/scripts/bump.py show
|
|
@@ -44,6 +46,12 @@ python3 .claude/skills/version-bump/scripts/bump.py show
|
|
|
44
46
|
|
|
45
47
|
Pass via stdin. All fields are optional — only non-empty sections are rendered.
|
|
46
48
|
|
|
49
|
+
Stdin is read until it closes, however slowly the JSON arrives. If it stays
|
|
50
|
+
open with no new input for `BUMP_STDIN_IDLE_SECONDS` (default 5), the bump
|
|
51
|
+
stops with an error and changes nothing, instead of hanging or dropping your
|
|
52
|
+
entries. `--no-stdin` (or `BUMP_NO_STDIN=1`) skips stdin and writes the empty
|
|
53
|
+
stub.
|
|
54
|
+
|
|
47
55
|
```json
|
|
48
56
|
{
|
|
49
57
|
"added": ["List of new features"],
|
|
@@ -57,7 +65,11 @@ Pass via stdin. All fields are optional — only non-empty sections are rendered
|
|
|
57
65
|
- `pyproject.toml` — the `version = "x.y.z"` field (single source of truth;
|
|
58
66
|
`steward/__init__.py` reads it via `importlib.metadata`, so there's no
|
|
59
67
|
separate `__version__` literal to keep in sync).
|
|
60
|
-
- `CHANGELOG.md` — inserts a new `## [x.y.z] - YYYY-MM-DD` entry
|
|
68
|
+
- `CHANGELOG.md` — inserts a new `## [x.y.z] - YYYY-MM-DD` entry above the
|
|
69
|
+
first `## [` release heading (headings inside fenced code blocks don't
|
|
70
|
+
count). A leading `## [Unreleased]` section stays on top, untouched; its
|
|
71
|
+
content is never moved into the release for you. A CHANGELOG with no release
|
|
72
|
+
heading yet gets the entry after its Unreleased section, or at the end.
|
|
61
73
|
|
|
62
74
|
The script does the rest. Pick a bump type from the diff (patch for fixes,
|
|
63
75
|
minor for new features, major for breaking changes), summarize the diff into
|
|
@@ -11,6 +11,7 @@ Usage:
|
|
|
11
11
|
bump.py minor # 0.1.0 -> 0.2.0
|
|
12
12
|
bump.py patch # 0.1.0 -> 0.1.1
|
|
13
13
|
bump.py show # print current version
|
|
14
|
+
bump.py patch --no-stdin # don't read stdin; insert the empty stub
|
|
14
15
|
|
|
15
16
|
Changelog entries are passed via stdin as a JSON object:
|
|
16
17
|
{
|
|
@@ -19,11 +20,22 @@ Changelog entries are passed via stdin as a JSON object:
|
|
|
19
20
|
"fixed": ["WHO reply index bug"]
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
If
|
|
23
|
+
If stdin is a terminal or empty, or --no-stdin (or BUMP_NO_STDIN=1) is given,
|
|
24
|
+
an empty stub is inserted. Stdin is read until it closes; if it stays open
|
|
25
|
+
with no new input for BUMP_STDIN_IDLE_SECONDS (default 5), the bump stops with
|
|
26
|
+
an error and changes nothing, rather than hanging or dropping entries.
|
|
27
|
+
|
|
28
|
+
The new section goes above the first ``## [`` release heading, ignoring
|
|
29
|
+
headings inside fenced code blocks. A leading ``## [Unreleased]`` section stays
|
|
30
|
+
on top, untouched; a CHANGELOG with no release heading yet gets the section
|
|
31
|
+
after its Unreleased section, or at the end.
|
|
23
32
|
"""
|
|
24
33
|
|
|
25
34
|
import json
|
|
35
|
+
import math
|
|
36
|
+
import os
|
|
26
37
|
import re
|
|
38
|
+
import select
|
|
27
39
|
import sys
|
|
28
40
|
from datetime import date
|
|
29
41
|
from pathlib import Path
|
|
@@ -78,17 +90,70 @@ def write_version(path: Path, old: str, new: str) -> None:
|
|
|
78
90
|
path.write_text(updated)
|
|
79
91
|
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
STDIN_IDLE_SECONDS_DEFAULT = 5.0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _env_flag(name: str) -> bool:
|
|
97
|
+
return os.environ.get(name, "").strip().lower() in ("1", "true", "yes")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _stdin_idle_seconds() -> float:
|
|
101
|
+
raw = os.environ.get("BUMP_STDIN_IDLE_SECONDS")
|
|
102
|
+
if raw is None:
|
|
103
|
+
return STDIN_IDLE_SECONDS_DEFAULT
|
|
104
|
+
try:
|
|
105
|
+
value = float(raw)
|
|
106
|
+
except ValueError:
|
|
107
|
+
value = 0.0
|
|
108
|
+
if not math.isfinite(value) or value <= 0:
|
|
109
|
+
print(
|
|
110
|
+
f"ERROR: BUMP_STDIN_IDLE_SECONDS must be a positive number, got {raw!r}",
|
|
111
|
+
file=sys.stderr,
|
|
112
|
+
)
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
return value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _read_stdin_until_eof(idle: float) -> str | None:
|
|
118
|
+
"""Read stdin to EOF; None if it stays open with no new input for ``idle`` s."""
|
|
119
|
+
fd = sys.stdin.fileno()
|
|
120
|
+
chunks = []
|
|
121
|
+
while True:
|
|
122
|
+
try:
|
|
123
|
+
ready, _, _ = select.select([fd], [], [], idle)
|
|
124
|
+
except (OSError, ValueError):
|
|
125
|
+
return sys.stdin.read() # select() can't poll this stdin (e.g. Windows): read as before
|
|
126
|
+
if not ready:
|
|
127
|
+
return None
|
|
128
|
+
chunk = os.read(fd, 65536)
|
|
129
|
+
if not chunk:
|
|
130
|
+
return b"".join(chunks).decode()
|
|
131
|
+
chunks.append(chunk)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def read_changelog_entries(no_stdin: bool = False) -> dict:
|
|
82
135
|
"""Read changelog entries from stdin as JSON."""
|
|
83
|
-
if sys.stdin.isatty():
|
|
136
|
+
if no_stdin or sys.stdin is None or sys.stdin.isatty():
|
|
137
|
+
return {}
|
|
138
|
+
idle = _stdin_idle_seconds()
|
|
139
|
+
raw = _read_stdin_until_eof(idle)
|
|
140
|
+
if raw is None:
|
|
141
|
+
print(
|
|
142
|
+
f"ERROR: stdin stayed open with no input for {idle:g}s and never closed; nothing "
|
|
143
|
+
"was changed. Pipe the changelog JSON (echo '{...}' | bump.py patch), or pass "
|
|
144
|
+
"--no-stdin (or set BUMP_NO_STDIN=1) for an empty stub.",
|
|
145
|
+
file=sys.stderr,
|
|
146
|
+
)
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
raw = raw.strip()
|
|
149
|
+
if not raw:
|
|
84
150
|
return {}
|
|
85
151
|
try:
|
|
86
|
-
raw = sys.stdin.read().strip()
|
|
87
|
-
if not raw:
|
|
88
|
-
return {}
|
|
89
152
|
return json.loads(raw)
|
|
90
153
|
except (json.JSONDecodeError, ValueError):
|
|
91
|
-
print(
|
|
154
|
+
print(
|
|
155
|
+
"WARNING: could not parse changelog JSON from stdin, using empty stub", file=sys.stderr
|
|
156
|
+
)
|
|
92
157
|
return {}
|
|
93
158
|
|
|
94
159
|
|
|
@@ -116,6 +181,32 @@ def format_changelog_section(new: str, entries: dict) -> str:
|
|
|
116
181
|
return "".join(chunks)
|
|
117
182
|
|
|
118
183
|
|
|
184
|
+
def _section_headings(lines: list[str]) -> list[tuple[int, str]]:
|
|
185
|
+
"""``## `` headings outside fenced code blocks, as (line index, line)."""
|
|
186
|
+
headings, fenced = [], False
|
|
187
|
+
for i, line in enumerate(lines):
|
|
188
|
+
if line.lstrip().startswith(("```", "~~~")):
|
|
189
|
+
fenced = not fenced
|
|
190
|
+
elif not fenced and line.startswith("## "):
|
|
191
|
+
headings.append((i, line))
|
|
192
|
+
return headings
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _insertion_index(lines: list[str]) -> int:
|
|
196
|
+
"""Line index for the new section: above the first ``## [`` release heading
|
|
197
|
+
that isn't Unreleased, so a leading Unreleased section stays on top; after
|
|
198
|
+
an Unreleased-only section, or at the end of a file with neither."""
|
|
199
|
+
headings = _section_headings(lines)
|
|
200
|
+
unreleased = re.compile(r"^## \[Unreleased\]", re.IGNORECASE)
|
|
201
|
+
releases = [i for i, line in headings if line.startswith("## [") and not unreleased.match(line)]
|
|
202
|
+
if releases:
|
|
203
|
+
return releases[0]
|
|
204
|
+
after = [i for i, line in headings if unreleased.match(line)]
|
|
205
|
+
if after:
|
|
206
|
+
return next((i for i, _ in headings if i > after[0]), len(lines))
|
|
207
|
+
return len(lines)
|
|
208
|
+
|
|
209
|
+
|
|
119
210
|
def update_changelog(project_root: Path, new: str, entries: dict) -> None:
|
|
120
211
|
"""Insert a new changelog entry into CHANGELOG.md."""
|
|
121
212
|
changelog = project_root / "CHANGELOG.md"
|
|
@@ -123,24 +214,25 @@ def update_changelog(project_root: Path, new: str, entries: dict) -> None:
|
|
|
123
214
|
print("No CHANGELOG.md found — skipping")
|
|
124
215
|
return
|
|
125
216
|
|
|
126
|
-
|
|
217
|
+
lines = changelog.read_text().splitlines(keepends=True)
|
|
218
|
+
idx = _insertion_index(lines)
|
|
219
|
+
before, after = "".join(lines[:idx]), "".join(lines[idx:])
|
|
127
220
|
new_entry = format_changelog_section(new, entries)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
print(f"Updated CHANGELOG.md with [{new}]")
|
|
134
|
-
else:
|
|
135
|
-
print("WARNING: could not find insertion point in CHANGELOG.md", file=sys.stderr)
|
|
221
|
+
if not after: # appending at the end of the file
|
|
222
|
+
before = before.rstrip("\n") + "\n\n" if before.strip() else ""
|
|
223
|
+
new_entry = new_entry.rstrip("\n") + "\n"
|
|
224
|
+
changelog.write_text(before + new_entry + after)
|
|
225
|
+
print(f"Updated CHANGELOG.md with [{new}]")
|
|
136
226
|
|
|
137
227
|
|
|
138
228
|
def main():
|
|
139
|
-
|
|
229
|
+
args = [a for a in sys.argv[1:] if a != "--no-stdin"]
|
|
230
|
+
no_stdin = len(args) < len(sys.argv) - 1 or _env_flag("BUMP_NO_STDIN")
|
|
231
|
+
if not args or args[0] in ("-h", "--help"):
|
|
140
232
|
print(__doc__.strip())
|
|
141
233
|
sys.exit(0)
|
|
142
234
|
|
|
143
|
-
part =
|
|
235
|
+
part = args[0].lower()
|
|
144
236
|
path = find_pyproject()
|
|
145
237
|
current = read_version(path)
|
|
146
238
|
|
|
@@ -149,7 +241,7 @@ def main():
|
|
|
149
241
|
sys.exit(0)
|
|
150
242
|
|
|
151
243
|
# Read changelog entries before bumping
|
|
152
|
-
entries = read_changelog_entries()
|
|
244
|
+
entries = read_changelog_entries(no_stdin)
|
|
153
245
|
|
|
154
246
|
new = bump(current, part)
|
|
155
247
|
write_version(path, current, new)
|
|
@@ -162,10 +254,12 @@ def main():
|
|
|
162
254
|
if init.exists():
|
|
163
255
|
init_text = init.read_text()
|
|
164
256
|
if f'__version__ = "{current}"' in init_text:
|
|
165
|
-
init.write_text(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
257
|
+
init.write_text(
|
|
258
|
+
init_text.replace(
|
|
259
|
+
f'__version__ = "{current}"',
|
|
260
|
+
f'__version__ = "{new}"',
|
|
261
|
+
)
|
|
262
|
+
)
|
|
169
263
|
print(f"Updated {init.relative_to(path.parent)}")
|
|
170
264
|
|
|
171
265
|
# Update changelog
|
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.26.23] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `version-bump`: `--no-stdin` (or `BUMP_NO_STDIN=1`) skips reading stdin and writes the empty changelog stub, for scripts, daemons and agent harnesses that hand the script a stdin nobody writes to. `BUMP_STDIN_IDLE_SECONDS` (default 5) sets how long stdin may stay silent before the bump gives up.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- `version-bump`: `bump.py` no longer hangs on a stdin that stays open, whether it sends nothing or only part of the JSON. Stdin is read until it closes, however slowly the JSON arrives; if it goes silent for `BUMP_STDIN_IDLE_SECONDS` first, the bump stops with an error and changes nothing, instead of hanging or silently dropping the entries. Piped JSON (`echo … | bump.py patch`) works as before.
|
|
17
|
+
- `version-bump`: the new `CHANGELOG.md` section goes above the first `## [` release heading, ignoring headings inside fenced code blocks. A leading `## [Unreleased]` section now stays on top, untouched; before, the release was inserted above it. A file whose first line is a `## [` heading is updated instead of refused (culture's vendored copy already did this), and a CHANGELOG with no release heading gets the section appended instead of a warning.
|
|
18
|
+
|
|
8
19
|
## [0.26.22] - 2026-10-24
|
|
9
20
|
|
|
10
21
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: guild-cli
|
|
3
|
-
Version: 0.26.
|
|
3
|
+
Version: 0.26.23
|
|
4
4
|
Summary: guildmaster — an agent and CLI that manages skills for the AgentCulture mesh.
|
|
5
5
|
Project-URL: Homepage, https://github.com/agentculture/guildmaster
|
|
6
6
|
Project-URL: Issues, https://github.com/agentculture/guildmaster/issues
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Behavioural tests for the vendored ``version-bump`` skill's ``bump.py``.
|
|
2
|
+
|
|
3
|
+
``bump.py`` is broadcast to every sibling, so these run the *actual* script in
|
|
4
|
+
a throwaway repo (a ``pyproject.toml`` + ``CHANGELOG.md``) as a subprocess.
|
|
5
|
+
The contract that matters:
|
|
6
|
+
|
|
7
|
+
- changelog JSON piped on stdin lands in the new section, however slowly it
|
|
8
|
+
arrives, as long as the producer keeps writing and then closes stdin;
|
|
9
|
+
- a stdin held open without input (a daemon or harness pipe) never hangs the
|
|
10
|
+
bump and never silently drops entries: it stops with an error and changes
|
|
11
|
+
nothing, unless ``--no-stdin`` / ``BUMP_NO_STDIN=1`` says no input is coming;
|
|
12
|
+
- the new section goes above the first ``## [`` release heading: a leading
|
|
13
|
+
``## [Unreleased]`` stays on top untouched, and ``## [`` lines in code
|
|
14
|
+
fences or a stray mid-file Unreleased heading are not insertion points.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
import time
|
|
23
|
+
from datetime import date
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
import pytest
|
|
27
|
+
|
|
28
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
29
|
+
BUMP = REPO_ROOT / ".claude" / "skills" / "version-bump" / "scripts" / "bump.py"
|
|
30
|
+
TODAY = date.today().isoformat()
|
|
31
|
+
FAST_IDLE = {"BUMP_STDIN_IDLE_SECONDS": "0.2"}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _repo(tmp_path: Path, changelog: str = "# Changelog\n\n## [0.1.0] - 2026-01-01\n") -> Path:
|
|
35
|
+
(tmp_path / "pyproject.toml").write_text('[project]\nname = "x"\nversion = "0.1.0"\n')
|
|
36
|
+
(tmp_path / "CHANGELOG.md").write_text(changelog)
|
|
37
|
+
return tmp_path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _env(**extra: str) -> dict:
|
|
41
|
+
env = {k: v for k, v in os.environ.items() if not k.startswith("BUMP_")}
|
|
42
|
+
env.update(extra)
|
|
43
|
+
return env
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _bump(repo: Path, stdin: str = "", *args: str, **env: str) -> subprocess.CompletedProcess:
|
|
47
|
+
return subprocess.run(
|
|
48
|
+
[sys.executable, str(BUMP), "patch", *args],
|
|
49
|
+
cwd=repo,
|
|
50
|
+
input=stdin,
|
|
51
|
+
capture_output=True,
|
|
52
|
+
text=True,
|
|
53
|
+
timeout=30,
|
|
54
|
+
env=_env(**env),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _bump_with_open_stdin(repo: Path, *args: str, first: str = "", **env: str):
|
|
59
|
+
"""Run a bump whose stdin stays open (optionally after writing ``first``)."""
|
|
60
|
+
proc = subprocess.Popen(
|
|
61
|
+
[sys.executable, str(BUMP), "patch", *args],
|
|
62
|
+
cwd=repo,
|
|
63
|
+
stdin=subprocess.PIPE,
|
|
64
|
+
stdout=subprocess.PIPE,
|
|
65
|
+
stderr=subprocess.PIPE,
|
|
66
|
+
text=True,
|
|
67
|
+
env=_env(**env),
|
|
68
|
+
)
|
|
69
|
+
try:
|
|
70
|
+
if first:
|
|
71
|
+
proc.stdin.write(first)
|
|
72
|
+
proc.stdin.flush()
|
|
73
|
+
proc.wait(timeout=30)
|
|
74
|
+
except subprocess.TimeoutExpired:
|
|
75
|
+
proc.kill()
|
|
76
|
+
raise
|
|
77
|
+
finally:
|
|
78
|
+
proc.stdin.close()
|
|
79
|
+
out, err = proc.stdout.read(), proc.stderr.read()
|
|
80
|
+
proc.stdout.close()
|
|
81
|
+
proc.stderr.close()
|
|
82
|
+
return proc.returncode, out, err
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _unchanged(repo: Path, changelog: str = "# Changelog\n\n## [0.1.0] - 2026-01-01\n") -> bool:
|
|
86
|
+
return (
|
|
87
|
+
'version = "0.1.0"' in (repo / "pyproject.toml").read_text()
|
|
88
|
+
and (repo / "CHANGELOG.md").read_text() == changelog
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---- stdin -------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_piped_entries_land_in_the_new_section(tmp_path):
|
|
96
|
+
repo = _repo(tmp_path)
|
|
97
|
+
result = _bump(repo, '{"fixed": ["The bug"]}')
|
|
98
|
+
assert result.returncode == 0, result.stderr
|
|
99
|
+
assert "0.1.0 -> 0.1.1" in result.stdout
|
|
100
|
+
assert 'version = "0.1.1"' in (repo / "pyproject.toml").read_text()
|
|
101
|
+
text = (repo / "CHANGELOG.md").read_text()
|
|
102
|
+
assert text == (
|
|
103
|
+
f"# Changelog\n\n## [0.1.1] - {TODAY}\n\n### Fixed\n\n- The bug\n\n"
|
|
104
|
+
"## [0.1.0] - 2026-01-01\n"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_slow_producer_that_keeps_writing_is_read_in_full(tmp_path):
|
|
109
|
+
# Review of #149: a fixed wait dropped JSON from a producer slower than it.
|
|
110
|
+
repo = _repo(tmp_path)
|
|
111
|
+
proc = subprocess.Popen(
|
|
112
|
+
[sys.executable, str(BUMP), "patch"],
|
|
113
|
+
cwd=repo,
|
|
114
|
+
stdin=subprocess.PIPE,
|
|
115
|
+
stdout=subprocess.PIPE,
|
|
116
|
+
stderr=subprocess.PIPE,
|
|
117
|
+
text=True,
|
|
118
|
+
env=_env(BUMP_STDIN_IDLE_SECONDS="1"),
|
|
119
|
+
)
|
|
120
|
+
for part in ('{"fixed": ', '["Slow"]', "}"):
|
|
121
|
+
time.sleep(0.4) # total 1.2 s: longer than the idle wait, but never idle that long
|
|
122
|
+
proc.stdin.write(part)
|
|
123
|
+
proc.stdin.flush()
|
|
124
|
+
out, err = proc.communicate(timeout=30)
|
|
125
|
+
assert proc.returncode == 0, err
|
|
126
|
+
assert "- Slow" in (repo / "CHANGELOG.md").read_text()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_idle_open_stdin_stops_with_an_error_and_changes_nothing(tmp_path):
|
|
130
|
+
repo = _repo(tmp_path)
|
|
131
|
+
code, _, err = _bump_with_open_stdin(repo, **FAST_IDLE)
|
|
132
|
+
assert code != 0
|
|
133
|
+
assert "--no-stdin" in err
|
|
134
|
+
assert _unchanged(repo)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_partial_input_then_held_open_stops_instead_of_hanging(tmp_path):
|
|
138
|
+
repo = _repo(tmp_path)
|
|
139
|
+
code, _, err = _bump_with_open_stdin(repo, first='{"fixed": ["x"', **FAST_IDLE)
|
|
140
|
+
assert code != 0
|
|
141
|
+
assert "never closed" in err
|
|
142
|
+
assert _unchanged(repo)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@pytest.mark.parametrize("how", ["flag", "env"])
|
|
146
|
+
def test_no_stdin_skips_reading_and_writes_the_stub(tmp_path, how):
|
|
147
|
+
repo = _repo(tmp_path)
|
|
148
|
+
args, env = (("--no-stdin",), {}) if how == "flag" else ((), {"BUMP_NO_STDIN": "1"})
|
|
149
|
+
start = time.monotonic()
|
|
150
|
+
code, out, err = _bump_with_open_stdin(repo, *args, BUMP_STDIN_IDLE_SECONDS="60", **env)
|
|
151
|
+
assert code == 0, err
|
|
152
|
+
assert time.monotonic() - start < 20 # never waited on the open pipe
|
|
153
|
+
assert "0.1.0 -> 0.1.1" in out
|
|
154
|
+
assert "### Added\n\n### Changed\n\n### Fixed\n" in (repo / "CHANGELOG.md").read_text()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@pytest.mark.parametrize("value", ["soon", "0", "-1", "nan", "inf"])
|
|
158
|
+
def test_invalid_idle_seconds_is_rejected(tmp_path, value):
|
|
159
|
+
repo = _repo(tmp_path)
|
|
160
|
+
result = _bump(repo, "", BUMP_STDIN_IDLE_SECONDS=value)
|
|
161
|
+
assert result.returncode != 0
|
|
162
|
+
assert "BUMP_STDIN_IDLE_SECONDS" in result.stderr
|
|
163
|
+
assert _unchanged(repo)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ---- CHANGELOG insertion -----------------------------------------------------
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_changelog_starting_with_a_section_is_still_updated(tmp_path):
|
|
170
|
+
repo = _repo(tmp_path, changelog="## [0.1.0] - 2026-01-01\n")
|
|
171
|
+
assert _bump(repo).returncode == 0
|
|
172
|
+
assert (repo / "CHANGELOG.md").read_text().startswith(f"## [0.1.1] - {TODAY}\n")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_leading_unreleased_stays_on_top_untouched(tmp_path):
|
|
176
|
+
# Unreleased content in consumer repos is often stale or a placeholder, so
|
|
177
|
+
# it is never moved into a release automatically.
|
|
178
|
+
unreleased = "## [Unreleased]\n\nNote.\n\n### Fixed\n\n- Pending\n\n"
|
|
179
|
+
repo = _repo(tmp_path, changelog=f"# Changelog\n\n{unreleased}## [0.1.0] - 2026-01-01\n")
|
|
180
|
+
result = _bump(repo, '{"fixed": ["From stdin"]}')
|
|
181
|
+
assert result.returncode == 0, result.stderr
|
|
182
|
+
assert (repo / "CHANGELOG.md").read_text() == (
|
|
183
|
+
f"# Changelog\n\n{unreleased}## [0.1.1] - {TODAY}\n\n### Fixed\n\n- From stdin\n\n"
|
|
184
|
+
"## [0.1.0] - 2026-01-01\n"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_unreleased_only_changelog_gets_the_section_after_it(tmp_path):
|
|
189
|
+
repo = _repo(tmp_path, changelog="# Changelog\n\n## [Unreleased]\n\n- Pending\n")
|
|
190
|
+
assert _bump(repo, '{"fixed": ["Bug"]}').returncode == 0
|
|
191
|
+
assert (repo / "CHANGELOG.md").read_text() == (
|
|
192
|
+
f"# Changelog\n\n## [Unreleased]\n\n- Pending\n\n## [0.1.1] - {TODAY}\n\n"
|
|
193
|
+
"### Fixed\n\n- Bug\n"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_empty_leading_unreleased_stays_on_top(tmp_path):
|
|
198
|
+
repo = _repo(tmp_path, changelog="# Changelog\n\n## [Unreleased]\n\n## [0.1.0] - 2026-01-01\n")
|
|
199
|
+
assert _bump(repo, '{"fixed": ["Bug"]}').returncode == 0
|
|
200
|
+
assert (repo / "CHANGELOG.md").read_text() == (
|
|
201
|
+
f"# Changelog\n\n## [Unreleased]\n\n## [0.1.1] - {TODAY}\n\n### Fixed\n\n- Bug\n\n"
|
|
202
|
+
"## [0.1.0] - 2026-01-01\n"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def test_stray_mid_file_unreleased_and_fenced_headings_are_not_insertion_points(tmp_path):
|
|
207
|
+
changelog = (
|
|
208
|
+
"# Changelog\n\nEntries look like:\n\n```markdown\n## [1.2.3] - 2026-01-01\n```\n\n"
|
|
209
|
+
"## [0.1.0] - 2026-01-01\n\n## [Unreleased]\n\n## [0.0.1] - 2025-01-01\n"
|
|
210
|
+
)
|
|
211
|
+
repo = _repo(tmp_path, changelog=changelog)
|
|
212
|
+
assert _bump(repo, '{"fixed": ["Bug"]}').returncode == 0
|
|
213
|
+
text = (repo / "CHANGELOG.md").read_text()
|
|
214
|
+
assert text.index("```\n\n## [0.1.1]") < text.index("## [0.1.0]")
|
|
215
|
+
assert text.count("## [Unreleased]") == 1
|
|
216
|
+
assert text.endswith("## [0.1.0] - 2026-01-01\n\n## [Unreleased]\n\n## [0.0.1] - 2025-01-01\n")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_changelog_without_a_version_heading_gets_the_section_appended(tmp_path):
|
|
220
|
+
repo = _repo(tmp_path, changelog="# Changelog\n\nAll notable changes.\n")
|
|
221
|
+
assert _bump(repo, '{"fixed": ["Bug"]}').returncode == 0
|
|
222
|
+
assert (repo / "CHANGELOG.md").read_text() == (
|
|
223
|
+
f"# Changelog\n\nAll notable changes.\n\n## [0.1.1] - {TODAY}\n\n### Fixed\n\n- Bug\n"
|
|
224
|
+
)
|
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/agent-config/data/backend-fingerprints.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/ask-colleague/scripts/ask-colleague.sh
RENAMED
|
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
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.claude/skills/pypi-maintainer/scripts/switch-source.sh
RENAMED
|
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
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/deliveries/template-supports-four-harnesses.json
RENAMED
|
File without changes
|
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/frames/template-supports-four-harnesses.json
RENAMED
|
File without changes
|
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.devague/plans/template-supports-four-harnesses.json
RENAMED
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/.eidetic/memory/culture-agent-template__public.jsonl
RENAMED
|
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
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/docs/plans/2026-09-05-template-supports-four-harnesses.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{guild_cli-0.26.22 → guild_cli-0.26.23}/docs/specs/2026-09-05-template-supports-four-harnesses.md
RENAMED
|
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
|