agent-wiki-cli 0.3.28__py3-none-any.whl

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 (47) hide show
  1. agent_wiki_cli-0.3.28.dist-info/METADATA +425 -0
  2. agent_wiki_cli-0.3.28.dist-info/RECORD +47 -0
  3. agent_wiki_cli-0.3.28.dist-info/WHEEL +5 -0
  4. agent_wiki_cli-0.3.28.dist-info/entry_points.txt +2 -0
  5. agent_wiki_cli-0.3.28.dist-info/licenses/LICENSE +21 -0
  6. agent_wiki_cli-0.3.28.dist-info/top_level.txt +1 -0
  7. llm_wiki_cli/__init__.py +7 -0
  8. llm_wiki_cli/cli.py +231 -0
  9. llm_wiki_cli/commands/__init__.py +1 -0
  10. llm_wiki_cli/commands/bootstrap_cmd.py +1072 -0
  11. llm_wiki_cli/commands/bump_cmd.py +55 -0
  12. llm_wiki_cli/commands/context_cmd.py +427 -0
  13. llm_wiki_cli/commands/extract_cmd.py +745 -0
  14. llm_wiki_cli/commands/generate_prompt_cmd.py +89 -0
  15. llm_wiki_cli/commands/hook_cmd.py +161 -0
  16. llm_wiki_cli/commands/init_cmd.py +92 -0
  17. llm_wiki_cli/commands/lint_cmd.py +294 -0
  18. llm_wiki_cli/commands/migrate_cmd.py +892 -0
  19. llm_wiki_cli/commands/release_cmd.py +163 -0
  20. llm_wiki_cli/commands/status_cmd.py +70 -0
  21. llm_wiki_cli/commands/sync_cmd.py +521 -0
  22. llm_wiki_cli/commands/trigger_cmd.py +205 -0
  23. llm_wiki_cli/commands/uninstall_cmd.py +221 -0
  24. llm_wiki_cli/commands/upgrade_cmd.py +196 -0
  25. llm_wiki_cli/config.py +318 -0
  26. llm_wiki_cli/extractors/__init__.py +46 -0
  27. llm_wiki_cli/extractors/common.py +90 -0
  28. llm_wiki_cli/extractors/go_extractor.py +143 -0
  29. llm_wiki_cli/extractors/go_scripts/go.mod +3 -0
  30. llm_wiki_cli/extractors/go_scripts/main.go +668 -0
  31. llm_wiki_cli/extractors/python_extractor.py +346 -0
  32. llm_wiki_cli/extractors/rust_extractor.py +143 -0
  33. llm_wiki_cli/extractors/rust_scripts/Cargo.lock +110 -0
  34. llm_wiki_cli/extractors/rust_scripts/Cargo.toml +11 -0
  35. llm_wiki_cli/extractors/rust_scripts/src/main.rs +803 -0
  36. llm_wiki_cli/extractors/ts_extractor.py +206 -0
  37. llm_wiki_cli/extractors/ts_scripts/extract.js +485 -0
  38. llm_wiki_cli/extractors/ts_scripts/package.json +10 -0
  39. llm_wiki_cli/services/__init__.py +0 -0
  40. llm_wiki_cli/services/circuit_breaker.py +79 -0
  41. llm_wiki_cli/services/io.py +47 -0
  42. llm_wiki_cli/services/lockfile.py +60 -0
  43. llm_wiki_cli/services/packages.py +173 -0
  44. llm_wiki_cli/services/paths.py +31 -0
  45. llm_wiki_cli/services/schema.py +214 -0
  46. llm_wiki_cli/services/secure_file.py +22 -0
  47. llm_wiki_cli/services/versioning.py +193 -0
@@ -0,0 +1,163 @@
1
+ """release_cmd — stamp the [Unreleased] CHANGELOG section with the current version.
2
+
3
+ Transforms::
4
+
5
+ ## [Unreleased]
6
+ ...changes...
7
+
8
+ ## [0.1.5] - 2026-04-11
9
+ ...
10
+
11
+ Into::
12
+
13
+ ## [Unreleased]
14
+
15
+ ## [0.1.6] - 2026-04-12
16
+ ...changes...
17
+
18
+ ## [0.1.5] - 2026-04-11
19
+ ...
20
+
21
+ And updates the reference links at the bottom of the file.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import re
26
+ import subprocess
27
+ import sys
28
+ from datetime import date
29
+ from pathlib import Path
30
+
31
+ from ..services.versioning import find_version_file, read_version
32
+
33
+ # Matches the [Unreleased] section heading
34
+ _UNRELEASED_RE = re.compile(r"^## \[Unreleased\]", re.MULTILINE)
35
+ # Matches any existing version reference link: [x.y.z]: https://...
36
+ _REF_LINK_RE = re.compile(r"^\[[\w.]+\]: https://.*$", re.MULTILINE)
37
+
38
+ _GITHUB_REPO_RE = re.compile(r"https://github\.com/([^/]+/[^/]+)/compare/")
39
+
40
+ # Matches content between [Unreleased] heading and the next ## heading or end-of-file ref-links
41
+ _UNRELEASED_BODY_RE = re.compile(
42
+ r"^## \[Unreleased\]\s*\n(.*?)(?=^## \[|^\[|\Z)",
43
+ re.MULTILINE | re.DOTALL,
44
+ )
45
+
46
+
47
+ def _unreleased_has_content(text: str) -> bool:
48
+ """Return True if the [Unreleased] section contains at least one non-blank line."""
49
+ m = _UNRELEASED_BODY_RE.search(text)
50
+ if not m:
51
+ return False
52
+ body = m.group(1)
53
+ return any(line.strip() for line in body.splitlines())
54
+
55
+
56
+ def _detect_repo_url(changelog_text: str) -> str | None:
57
+ """Extract the GitHub compare base URL from existing reference links."""
58
+ m = _GITHUB_REPO_RE.search(changelog_text)
59
+ if m:
60
+ return f"https://github.com/{m.group(1)}"
61
+ return None
62
+
63
+
64
+ def stamp_changelog(changelog_path: Path, version: str, today: str | None = None) -> tuple[str, bool]:
65
+ """Stamp the [Unreleased] section with *version* and return ``(new_text, stamped)``.
66
+
67
+ *stamped* is ``False`` (and the original text is returned unchanged) when the
68
+ ``[Unreleased]`` section is empty — i.e. the agent has not written any entries yet.
69
+
70
+ Raises ``ValueError`` if no ``## [Unreleased]`` section is found.
71
+ """
72
+ text = changelog_path.read_text(encoding="utf-8")
73
+ release_date = today or date.today().isoformat()
74
+
75
+ if not _UNRELEASED_RE.search(text):
76
+ raise ValueError("No '## [Unreleased]' section found in CHANGELOG.")
77
+
78
+ # Skip stamp when [Unreleased] has no substantive content yet
79
+ if not _unreleased_has_content(text):
80
+ return text, False
81
+
82
+ # Replace the first [Unreleased] heading with [Unreleased] + new version heading
83
+ new_version_heading = f"## [Unreleased]\n\n## [{version}] - {release_date}"
84
+ new_text = _UNRELEASED_RE.sub(new_version_heading, text, count=1)
85
+
86
+ # Update reference links section
87
+ repo_url = _detect_repo_url(text)
88
+ if repo_url:
89
+ # Remove all existing reference links (we'll rebuild them)
90
+ new_text = _REF_LINK_RE.sub("", new_text).rstrip() + "\n"
91
+
92
+ # Find previously highest version to build the compare URL for new version
93
+ # Collect all version tags already mentioned in headings
94
+ heading_versions = list(dict.fromkeys(
95
+ re.findall(r"## \[(\d+\.\d+\.\d+)\]", new_text)
96
+ ))
97
+
98
+ links: list[str] = []
99
+ links.append(f"[Unreleased]: {repo_url}/compare/v{version}...HEAD")
100
+
101
+ # Build compare links between consecutive versions (newest first)
102
+ for i, ver in enumerate(heading_versions):
103
+ if i + 1 < len(heading_versions):
104
+ prev = heading_versions[i + 1]
105
+ links.append(f"[{ver}]: {repo_url}/compare/v{prev}...v{ver}")
106
+ else:
107
+ links.append(f"[{ver}]: {repo_url}/releases/tag/v{ver}")
108
+
109
+ new_text += "\n" + "\n".join(links) + "\n"
110
+
111
+ return new_text, True
112
+
113
+
114
+ def run(args):
115
+ root = getattr(args, "root", ".")
116
+ changelog_path = Path(getattr(args, "changelog", "CHANGELOG.md"))
117
+
118
+ if not changelog_path.exists():
119
+ print(f"Error: {changelog_path} not found.")
120
+ sys.exit(1)
121
+
122
+ # Read version from project file
123
+ version_file = find_version_file(root)
124
+ if version_file is None:
125
+ print("Error: No version file found (pyproject.toml, setup.cfg, package.json, VERSION).")
126
+ sys.exit(1)
127
+
128
+ version = read_version(version_file)
129
+ if version is None:
130
+ print(f"Error: Could not parse version from {version_file}")
131
+ sys.exit(1)
132
+
133
+ try:
134
+ new_text, stamped = stamp_changelog(changelog_path, version)
135
+ except ValueError as e:
136
+ print(f"Error: {e}")
137
+ sys.exit(1)
138
+
139
+ if not stamped:
140
+ print("CHANGELOG.md: [Unreleased] is empty — nothing to stamp (run after the agent adds entries).")
141
+ return
142
+
143
+ changelog_path.write_bytes(new_text.encode("utf-8"))
144
+ print(f"CHANGELOG.md: [Unreleased] → [{version}] ({date.today().isoformat()})")
145
+
146
+ if getattr(args, "stage", False):
147
+ try:
148
+ subprocess.run(
149
+ ["git", "add", str(changelog_path)],
150
+ check=True,
151
+ capture_output=True,
152
+ text=True,
153
+ )
154
+ except FileNotFoundError:
155
+ print("Error: git not found; could not stage changelog.", file=sys.stderr)
156
+ sys.exit(1)
157
+ except subprocess.CalledProcessError as exc:
158
+ detail = (exc.stderr or exc.stdout or "").strip()
159
+ print(f"Error: git add failed for {changelog_path}.", file=sys.stderr)
160
+ if detail:
161
+ print(detail, file=sys.stderr)
162
+ sys.exit(1)
163
+ print(f"Staged: {changelog_path}")
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from ..config import DEFAULT_WIKI_DIR, IDE_AGENTS, read_config, get_agent_config_path
7
+ from ..services import circuit_breaker
8
+
9
+
10
+ def run(args) -> None:
11
+ wiki_dir = getattr(args, "wiki_dir", DEFAULT_WIKI_DIR)
12
+ wiki_path = Path(wiki_dir)
13
+ git_dir = Path(".git")
14
+
15
+ print("LLM Wiki Status")
16
+ print("=" * 40)
17
+
18
+ # Wiki directory
19
+ if wiki_path.exists():
20
+ entity_count = len(list((wiki_path / "entities").glob("*.md"))) if (wiki_path / "entities").exists() else 0
21
+ module_count = len(list((wiki_path / "modules").glob("*.md"))) if (wiki_path / "modules").exists() else 0
22
+ workflow_count = len(list((wiki_path / "workflows").glob("*.md"))) if (wiki_path / "workflows").exists() else 0
23
+ print(f"Wiki directory: {wiki_dir} (exists)")
24
+ print(f" Entities: {entity_count}")
25
+ print(f" Modules: {module_count}")
26
+ print(f" Workflows: {workflow_count}")
27
+ else:
28
+ print(f"Wiki directory: {wiki_dir} (not found)")
29
+
30
+ # Agent config
31
+ agent_config = get_agent_config_path(wiki_dir)
32
+ if agent_config.exists():
33
+ config = read_config(wiki_dir)
34
+ agent = config.get("agent", "unknown")
35
+ mode = "IDE" if agent in IDE_AGENTS else "CLI"
36
+ print(f"Agent: {agent} ({mode})")
37
+ hints = config.get("quality_hints", True)
38
+ print(f"Quality hints: {'enabled' if hints else 'disabled'}")
39
+ else:
40
+ print("Agent: not configured (run `llm-wiki init --agent <agent>`)")
41
+
42
+ # Hooks
43
+ hooks_dir = git_dir / "hooks"
44
+ if hooks_dir.exists():
45
+ installed = []
46
+ for hook_name in ["post-commit", "pre-commit", "pre-push"]:
47
+ hook_file = hooks_dir / hook_name
48
+ if hook_file.exists():
49
+ content = hook_file.read_text(encoding="utf-8")
50
+ if "LLM Wiki" in content:
51
+ installed.append(hook_name)
52
+ if installed:
53
+ print(f"Hooks: {', '.join(installed)}")
54
+ else:
55
+ print("Hooks: none installed")
56
+ else:
57
+ print("Hooks: no .git/hooks directory")
58
+
59
+ # Circuit breaker
60
+ if git_dir.exists():
61
+ state = circuit_breaker.load_state(git_dir)
62
+ breaker_state = state.get("state", "closed")
63
+ failures = state.get("consecutive_failures", 0)
64
+ if breaker_state == "open":
65
+ print(f"Circuit breaker: OPEN ({failures} consecutive failures)")
66
+ print(" Run `llm-wiki trigger-agent --reset-breaker` to re-enable")
67
+ else:
68
+ print(f"Circuit breaker: closed ({failures} recent failures)")
69
+ else:
70
+ print("Circuit breaker: no .git directory")