buildlog 0.9.0__tar.gz → 0.10.0__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.
- {buildlog-0.9.0 → buildlog-0.10.0}/PKG-INFO +22 -22
- {buildlog-0.9.0 → buildlog-0.10.0}/README.md +20 -19
- {buildlog-0.9.0 → buildlog-0.10.0}/post_gen.py +10 -5
- {buildlog-0.9.0 → buildlog-0.10.0}/pyproject.toml +4 -6
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/cli.py +268 -26
- buildlog-0.10.0/src/buildlog/constants.py +121 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/core/__init__.py +44 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/core/operations.py +1170 -0
- buildlog-0.10.0/src/buildlog/data/seeds/bragi.yaml +61 -0
- buildlog-0.10.0/src/buildlog/mcp/__init__.py +65 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/mcp/server.py +36 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/mcp/tools.py +526 -12
- buildlog-0.10.0/template/buildlog/.gitkeep +0 -0
- buildlog-0.10.0/template/buildlog/assets/.gitkeep +0 -0
- buildlog-0.9.0/src/buildlog/mcp/__init__.py +0 -17
- {buildlog-0.9.0 → buildlog-0.10.0}/.gitignore +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/LICENSE +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/copier.yml +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/__init__.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/confidence.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/core/bandit.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/data/__init__.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/data/seeds/security_karen.yaml +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/data/seeds/test_terrorist.yaml +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/distill.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/embeddings.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/__init__.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/bandit.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/confidence.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/embeddings.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/experiments.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/engine/types.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/llm.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/__init__.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/base.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/claude_md.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/continue_dev.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/copilot.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/cursor.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/settings_json.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/skill.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/tracking.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/render/windsurf.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/__init__.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/categorizers.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/extractors.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/generators.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/llm_extractor.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/models.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/pipeline.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seed_engine/sources.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/seeds.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/skills.py +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/src/buildlog/stats.py +0 -0
- {buildlog-0.9.0/template/buildlog → buildlog-0.10.0/template/buildlog/.buildlog}/.gitkeep +0 -0
- {buildlog-0.9.0/template/buildlog/assets → buildlog-0.10.0/template/buildlog/.buildlog/seeds}/.gitkeep +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/template/buildlog/2026-01-01-example.md +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/template/buildlog/BUILDLOG_SYSTEM.md +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/template/buildlog/_TEMPLATE.md +0 -0
- {buildlog-0.9.0 → buildlog-0.10.0}/template/buildlog/_TEMPLATE_QUICK.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: buildlog
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Engineering notebook for AI-assisted development
|
|
5
5
|
Project-URL: Homepage, https://github.com/Peleke/buildlog-template
|
|
6
6
|
Project-URL: Repository, https://github.com/Peleke/buildlog-template
|
|
@@ -22,12 +22,12 @@ Classifier: Topic :: Software Development :: Documentation
|
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
Requires-Dist: click>=8.0.0
|
|
24
24
|
Requires-Dist: copier>=9.0.0
|
|
25
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
26
|
Requires-Dist: numpy>=1.21.0
|
|
26
27
|
Requires-Dist: pymupdf>=1.26.7
|
|
27
28
|
Requires-Dist: pyyaml>=6.0.0
|
|
28
29
|
Provides-Extra: all
|
|
29
30
|
Requires-Dist: anthropic>=0.40.0; extra == 'all'
|
|
30
|
-
Requires-Dist: mcp>=1.0.0; extra == 'all'
|
|
31
31
|
Requires-Dist: ollama>=0.4.0; extra == 'all'
|
|
32
32
|
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
33
33
|
Requires-Dist: sentence-transformers>=2.2.0; extra == 'all'
|
|
@@ -51,7 +51,6 @@ Provides-Extra: llm
|
|
|
51
51
|
Requires-Dist: anthropic>=0.40.0; extra == 'llm'
|
|
52
52
|
Requires-Dist: ollama>=0.4.0; extra == 'llm'
|
|
53
53
|
Provides-Extra: mcp
|
|
54
|
-
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
55
54
|
Provides-Extra: ollama
|
|
56
55
|
Requires-Dist: ollama>=0.4.0; extra == 'ollama'
|
|
57
56
|
Provides-Extra: openai
|
|
@@ -164,22 +163,29 @@ The roadmap: contextual bandits (now) -> richer policy models -> longer-horizon
|
|
|
164
163
|
|
|
165
164
|
## Installation
|
|
166
165
|
|
|
166
|
+
### Quick start
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pip install buildlog # MCP server included by default
|
|
170
|
+
buildlog init --defaults # scaffold project, register MCP, update CLAUDE.md
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
That's it. Claude Code will now have access to all 29 buildlog tools.
|
|
174
|
+
|
|
167
175
|
### Global install (recommended)
|
|
168
176
|
|
|
169
177
|
```bash
|
|
170
|
-
uv tool install
|
|
178
|
+
uv tool install buildlog # or: pipx install buildlog
|
|
171
179
|
```
|
|
172
180
|
|
|
173
|
-
This puts `buildlog` and `buildlog-mcp` on your PATH. Works from any directory.
|
|
181
|
+
This puts `buildlog` and `buildlog-mcp` on your PATH. Works from any directory.
|
|
174
182
|
|
|
175
183
|
### Per-project (virtual environment)
|
|
176
184
|
|
|
177
185
|
```bash
|
|
178
|
-
uv pip install
|
|
186
|
+
uv pip install buildlog # or: pip install buildlog
|
|
179
187
|
```
|
|
180
188
|
|
|
181
|
-
Omit `[mcp]` if you only need the CLI.
|
|
182
|
-
|
|
183
189
|
### For JS/TS projects
|
|
184
190
|
|
|
185
191
|
```bash
|
|
@@ -188,17 +194,11 @@ npx @peleke.s/buildlog init
|
|
|
188
194
|
|
|
189
195
|
### MCP server for Claude Code
|
|
190
196
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"buildlog": {
|
|
197
|
-
"command": "buildlog-mcp",
|
|
198
|
-
"args": []
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
197
|
+
`buildlog init` auto-registers the MCP server. For existing projects:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
buildlog init-mcp # register MCP in .claude/settings.json
|
|
201
|
+
buildlog mcp-test # verify all 29 tools are registered
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
This exposes buildlog tools (seeds, skills, experiments, gauntlet, bandit status) to any Claude Code session.
|
|
@@ -206,10 +206,10 @@ This exposes buildlog tools (seeds, skills, experiments, gauntlet, bandit status
|
|
|
206
206
|
## Quick Start
|
|
207
207
|
|
|
208
208
|
```bash
|
|
209
|
-
buildlog init
|
|
210
|
-
buildlog new my-feature
|
|
209
|
+
buildlog init --defaults # scaffold + MCP + CLAUDE.md
|
|
210
|
+
buildlog new my-feature # start a session
|
|
211
211
|
# ... work ...
|
|
212
|
-
buildlog
|
|
212
|
+
buildlog commit -m "feat: add auth"
|
|
213
213
|
buildlog experiment start
|
|
214
214
|
# ... work across sessions ...
|
|
215
215
|
buildlog experiment end
|
|
@@ -104,22 +104,29 @@ The roadmap: contextual bandits (now) -> richer policy models -> longer-horizon
|
|
|
104
104
|
|
|
105
105
|
## Installation
|
|
106
106
|
|
|
107
|
+
### Quick start
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install buildlog # MCP server included by default
|
|
111
|
+
buildlog init --defaults # scaffold project, register MCP, update CLAUDE.md
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
That's it. Claude Code will now have access to all 29 buildlog tools.
|
|
115
|
+
|
|
107
116
|
### Global install (recommended)
|
|
108
117
|
|
|
109
118
|
```bash
|
|
110
|
-
uv tool install
|
|
119
|
+
uv tool install buildlog # or: pipx install buildlog
|
|
111
120
|
```
|
|
112
121
|
|
|
113
|
-
This puts `buildlog` and `buildlog-mcp` on your PATH. Works from any directory.
|
|
122
|
+
This puts `buildlog` and `buildlog-mcp` on your PATH. Works from any directory.
|
|
114
123
|
|
|
115
124
|
### Per-project (virtual environment)
|
|
116
125
|
|
|
117
126
|
```bash
|
|
118
|
-
uv pip install
|
|
127
|
+
uv pip install buildlog # or: pip install buildlog
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
Omit `[mcp]` if you only need the CLI.
|
|
122
|
-
|
|
123
130
|
### For JS/TS projects
|
|
124
131
|
|
|
125
132
|
```bash
|
|
@@ -128,17 +135,11 @@ npx @peleke.s/buildlog init
|
|
|
128
135
|
|
|
129
136
|
### MCP server for Claude Code
|
|
130
137
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"buildlog": {
|
|
137
|
-
"command": "buildlog-mcp",
|
|
138
|
-
"args": []
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
138
|
+
`buildlog init` auto-registers the MCP server. For existing projects:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
buildlog init-mcp # register MCP in .claude/settings.json
|
|
142
|
+
buildlog mcp-test # verify all 29 tools are registered
|
|
142
143
|
```
|
|
143
144
|
|
|
144
145
|
This exposes buildlog tools (seeds, skills, experiments, gauntlet, bandit status) to any Claude Code session.
|
|
@@ -146,10 +147,10 @@ This exposes buildlog tools (seeds, skills, experiments, gauntlet, bandit status
|
|
|
146
147
|
## Quick Start
|
|
147
148
|
|
|
148
149
|
```bash
|
|
149
|
-
buildlog init
|
|
150
|
-
buildlog new my-feature
|
|
150
|
+
buildlog init --defaults # scaffold + MCP + CLAUDE.md
|
|
151
|
+
buildlog new my-feature # start a session
|
|
151
152
|
# ... work ...
|
|
152
|
-
buildlog
|
|
153
|
+
buildlog commit -m "feat: add auth"
|
|
153
154
|
buildlog experiment start
|
|
154
155
|
# ... work across sessions ...
|
|
155
156
|
buildlog experiment end
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
try:
|
|
7
|
+
from buildlog.constants import CLAUDE_MD_BUILDLOG_SECTION
|
|
8
|
+
except ImportError:
|
|
9
|
+
# Fallback for when buildlog isn't installed (e.g., copier from GitHub)
|
|
10
|
+
CLAUDE_MD_BUILDLOG_SECTION = """
|
|
7
11
|
## Build Journal
|
|
8
12
|
|
|
9
13
|
After completing significant work (features, debugging sessions, deployments,
|
|
@@ -40,15 +44,16 @@ def main():
|
|
|
40
44
|
|
|
41
45
|
content = claude_md.read_text()
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
# Check for either old or new section marker
|
|
48
|
+
if "## buildlog Integration" in content or "## Build Journal" in content:
|
|
49
|
+
print("buildlog section already exists in CLAUDE.md")
|
|
45
50
|
return
|
|
46
51
|
|
|
47
52
|
# Append to end of file
|
|
48
53
|
with open(claude_md, "a") as f:
|
|
49
|
-
f.write("\n" +
|
|
54
|
+
f.write("\n" + CLAUDE_MD_BUILDLOG_SECTION)
|
|
50
55
|
|
|
51
|
-
print("Added
|
|
56
|
+
print("Added buildlog Integration section to CLAUDE.md")
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
if __name__ == "__main__":
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "buildlog"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "Engineering notebook for AI-assisted development"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -32,6 +32,7 @@ dependencies = [
|
|
|
32
32
|
"pyyaml>=6.0.0",
|
|
33
33
|
"numpy>=1.21.0",
|
|
34
34
|
"pymupdf>=1.26.7",
|
|
35
|
+
"mcp>=1.0.0",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
|
@@ -60,15 +61,12 @@ llm = [
|
|
|
60
61
|
"ollama>=0.4.0",
|
|
61
62
|
"anthropic>=0.40.0",
|
|
62
63
|
]
|
|
63
|
-
# MCP server for Claude Code integration
|
|
64
|
-
mcp = [
|
|
65
|
-
"mcp>=1.0.0",
|
|
66
|
-
]
|
|
64
|
+
# MCP server for Claude Code integration (now a default dependency, kept for backwards compat)
|
|
65
|
+
mcp = []
|
|
67
66
|
# All optional features
|
|
68
67
|
all = [
|
|
69
68
|
"sentence-transformers>=2.2.0",
|
|
70
69
|
"openai>=1.0.0",
|
|
71
|
-
"mcp>=1.0.0",
|
|
72
70
|
"ollama>=0.4.0",
|
|
73
71
|
"anthropic>=0.40.0",
|
|
74
72
|
]
|
|
@@ -50,12 +50,13 @@ def main():
|
|
|
50
50
|
|
|
51
51
|
@main.command()
|
|
52
52
|
@click.option("--no-claude-md", is_flag=True, help="Don't update CLAUDE.md")
|
|
53
|
+
@click.option("--no-mcp", is_flag=True, help="Don't register MCP server")
|
|
53
54
|
@click.option(
|
|
54
55
|
"--defaults",
|
|
55
56
|
is_flag=True,
|
|
56
57
|
help="Use default values for all prompts (non-interactive)",
|
|
57
58
|
)
|
|
58
|
-
def init(no_claude_md: bool, defaults: bool):
|
|
59
|
+
def init(no_claude_md: bool, no_mcp: bool, defaults: bool):
|
|
59
60
|
"""Initialize buildlog in the current directory.
|
|
60
61
|
|
|
61
62
|
Sets up the buildlog/ directory with templates and optionally
|
|
@@ -109,23 +110,40 @@ def init(no_claude_md: bool, defaults: bool):
|
|
|
109
110
|
click.echo("Failed to initialize buildlog.", err=True)
|
|
110
111
|
raise SystemExit(1)
|
|
111
112
|
|
|
113
|
+
# Ensure .buildlog/ directory exists (copier skips dot-prefixed paths)
|
|
114
|
+
dot_buildlog = buildlog_dir / ".buildlog"
|
|
115
|
+
dot_buildlog.mkdir(exist_ok=True)
|
|
116
|
+
(dot_buildlog / "seeds").mkdir(exist_ok=True)
|
|
117
|
+
|
|
112
118
|
# Update CLAUDE.md if it exists and user didn't opt out
|
|
113
119
|
if not no_claude_md:
|
|
114
120
|
claude_md = Path("CLAUDE.md")
|
|
115
121
|
if claude_md.exists():
|
|
116
122
|
content = claude_md.read_text()
|
|
117
|
-
if
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
if (
|
|
124
|
+
"## buildlog Integration" not in content
|
|
125
|
+
and "## Build Journal" not in content
|
|
126
|
+
):
|
|
127
|
+
try:
|
|
128
|
+
from buildlog.constants import CLAUDE_MD_BUILDLOG_SECTION
|
|
129
|
+
|
|
130
|
+
section = CLAUDE_MD_BUILDLOG_SECTION
|
|
131
|
+
except ImportError:
|
|
132
|
+
section = (
|
|
133
|
+
"\n## Build Journal\n\n"
|
|
134
|
+
"After completing significant work (features, debugging "
|
|
135
|
+
"sessions, deployments,\n"
|
|
136
|
+
"2+ hour focused sessions), write a build journal entry.\n\n"
|
|
137
|
+
"**Location:** `buildlog/YYYY-MM-DD-{slug}.md`\n"
|
|
138
|
+
"**Template:** `buildlog/_TEMPLATE.md`\n"
|
|
139
|
+
)
|
|
126
140
|
with open(claude_md, "a") as f:
|
|
127
141
|
f.write(section)
|
|
128
|
-
click.echo("Added
|
|
142
|
+
click.echo("Added buildlog Integration section to CLAUDE.md")
|
|
143
|
+
|
|
144
|
+
# Register MCP server unless opted out
|
|
145
|
+
if not no_mcp:
|
|
146
|
+
_init_mcp()
|
|
129
147
|
|
|
130
148
|
click.echo("\n✓ buildlog initialized!")
|
|
131
149
|
click.echo()
|
|
@@ -142,12 +160,125 @@ def init(no_claude_md: bool, defaults: bool):
|
|
|
142
160
|
click.echo("Start now: buildlog new my-first-task --quick")
|
|
143
161
|
|
|
144
162
|
|
|
163
|
+
def _init_mcp(settings_path: Path | None = None, global_mode: bool = False) -> None:
|
|
164
|
+
"""Register buildlog as an MCP server in settings.json.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
settings_path: Path to settings.json. Defaults to .claude/settings.json
|
|
168
|
+
global_mode: If True, display global-specific messaging
|
|
169
|
+
"""
|
|
170
|
+
import json as json_module
|
|
171
|
+
|
|
172
|
+
if settings_path is None:
|
|
173
|
+
settings_path = Path(".claude") / "settings.json"
|
|
174
|
+
|
|
175
|
+
location = "~/.claude/settings.json" if global_mode else ".claude/settings.json"
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
if settings_path.exists():
|
|
179
|
+
try:
|
|
180
|
+
data = json_module.loads(settings_path.read_text())
|
|
181
|
+
except json_module.JSONDecodeError:
|
|
182
|
+
click.echo(
|
|
183
|
+
f"Warning: {location} is malformed, skipping MCP registration",
|
|
184
|
+
err=True,
|
|
185
|
+
)
|
|
186
|
+
return
|
|
187
|
+
else:
|
|
188
|
+
data = {}
|
|
189
|
+
|
|
190
|
+
if "mcpServers" not in data:
|
|
191
|
+
data["mcpServers"] = {}
|
|
192
|
+
|
|
193
|
+
if "buildlog" in data["mcpServers"]:
|
|
194
|
+
click.echo(f"buildlog MCP server already registered in {location}")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
data["mcpServers"]["buildlog"] = {"command": "buildlog-mcp", "args": []}
|
|
198
|
+
|
|
199
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
200
|
+
settings_path.write_text(json_module.dumps(data, indent=2) + "\n")
|
|
201
|
+
click.echo(f"Registered buildlog MCP server in {location}")
|
|
202
|
+
if global_mode:
|
|
203
|
+
click.echo("Claude Code now has access to buildlog tools in all projects.")
|
|
204
|
+
except Exception as e:
|
|
205
|
+
click.echo(f"Warning: could not register MCP server: {e}", err=True)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@main.command("init-mcp")
|
|
209
|
+
@click.option(
|
|
210
|
+
"--global",
|
|
211
|
+
"global_",
|
|
212
|
+
is_flag=True,
|
|
213
|
+
help="Register globally in ~/.claude/settings.json (works in any project)",
|
|
214
|
+
)
|
|
215
|
+
def init_mcp(global_: bool):
|
|
216
|
+
"""Register buildlog as an MCP server for Claude Code.
|
|
217
|
+
|
|
218
|
+
Creates or updates .claude/settings.json with the buildlog MCP
|
|
219
|
+
server configuration. Idempotent — safe to run multiple times.
|
|
220
|
+
|
|
221
|
+
Use --global to register in ~/.claude/settings.json so buildlog
|
|
222
|
+
tools are available in every project without per-project init.
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
|
|
226
|
+
buildlog init-mcp # local (current project)
|
|
227
|
+
buildlog init-mcp --global # global (all projects)
|
|
228
|
+
"""
|
|
229
|
+
if global_:
|
|
230
|
+
settings_path = Path.home() / ".claude" / "settings.json"
|
|
231
|
+
_init_mcp(settings_path=settings_path, global_mode=True)
|
|
232
|
+
else:
|
|
233
|
+
_init_mcp()
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@main.command("mcp-test")
|
|
237
|
+
def mcp_test():
|
|
238
|
+
"""Verify the MCP server starts and all tools are registered.
|
|
239
|
+
|
|
240
|
+
Checks that the buildlog-mcp server can be imported and lists
|
|
241
|
+
all registered tools. Exits 0 if all 29 tools are found, 1 otherwise.
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
|
|
245
|
+
buildlog mcp-test
|
|
246
|
+
"""
|
|
247
|
+
try:
|
|
248
|
+
from buildlog.mcp.server import mcp as mcp_server
|
|
249
|
+
except ImportError:
|
|
250
|
+
click.echo("MCP not installed. Run: pip install buildlog", err=True)
|
|
251
|
+
raise SystemExit(1)
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
# FastMCP stores tools internally
|
|
255
|
+
tools = mcp_server._tool_manager._tools
|
|
256
|
+
tool_names = sorted(tools.keys())
|
|
257
|
+
except AttributeError:
|
|
258
|
+
# Fallback: try to count via the public API pattern
|
|
259
|
+
click.echo("Warning: could not inspect tools via internal API", err=True)
|
|
260
|
+
tool_names = []
|
|
261
|
+
|
|
262
|
+
expected = 29
|
|
263
|
+
click.echo(f"buildlog MCP server: {len(tool_names)} tools registered")
|
|
264
|
+
for name in tool_names:
|
|
265
|
+
click.echo(f" {name}")
|
|
266
|
+
|
|
267
|
+
if len(tool_names) >= expected:
|
|
268
|
+
click.echo(f"\nAll {expected} tools registered.")
|
|
269
|
+
raise SystemExit(0)
|
|
270
|
+
else:
|
|
271
|
+
click.echo(f"\nExpected {expected} tools, found {len(tool_names)}.", err=True)
|
|
272
|
+
raise SystemExit(1)
|
|
273
|
+
|
|
274
|
+
|
|
145
275
|
@main.command()
|
|
146
276
|
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
147
277
|
def overview(output_json: bool):
|
|
148
278
|
"""Show the full state of your buildlog at a glance.
|
|
149
279
|
|
|
150
280
|
Entries, skills, promoted rules, experiments — everything in one view.
|
|
281
|
+
Works even without buildlog init (shows uninitialized state).
|
|
151
282
|
|
|
152
283
|
Examples:
|
|
153
284
|
|
|
@@ -158,9 +289,38 @@ def overview(output_json: bool):
|
|
|
158
289
|
|
|
159
290
|
buildlog_dir = Path("buildlog")
|
|
160
291
|
|
|
292
|
+
# Handle uninitialized state gracefully
|
|
161
293
|
if not buildlog_dir.exists():
|
|
162
|
-
|
|
163
|
-
|
|
294
|
+
result = {
|
|
295
|
+
"initialized": False,
|
|
296
|
+
"entries": 0,
|
|
297
|
+
"skills": {
|
|
298
|
+
"total": 0,
|
|
299
|
+
"by_confidence": {},
|
|
300
|
+
"promoted": 0,
|
|
301
|
+
"rejected": 0,
|
|
302
|
+
"pending": 0,
|
|
303
|
+
},
|
|
304
|
+
"active_session": None,
|
|
305
|
+
"render_targets": [],
|
|
306
|
+
"message": "buildlog not initialized. Run 'buildlog init' to enable full features.",
|
|
307
|
+
}
|
|
308
|
+
if output_json:
|
|
309
|
+
click.echo(json_module.dumps(result, indent=2))
|
|
310
|
+
else:
|
|
311
|
+
click.echo("buildlog overview")
|
|
312
|
+
click.echo("=" * 40)
|
|
313
|
+
click.echo(" Status: Not initialized")
|
|
314
|
+
click.echo()
|
|
315
|
+
click.echo("Get started:")
|
|
316
|
+
click.echo(" buildlog init --defaults # Initialize buildlog")
|
|
317
|
+
click.echo()
|
|
318
|
+
click.echo("Or use globally without init:")
|
|
319
|
+
click.echo(" buildlog init-mcp --global # Register MCP server globally")
|
|
320
|
+
click.echo(
|
|
321
|
+
" buildlog gauntlet list # Review personas work without init"
|
|
322
|
+
)
|
|
323
|
+
return
|
|
164
324
|
|
|
165
325
|
# Count entries
|
|
166
326
|
entries = sorted(buildlog_dir.glob("20??-??-??-*.md"))
|
|
@@ -208,6 +368,7 @@ def overview(output_json: bool):
|
|
|
208
368
|
from buildlog.render import RENDERERS
|
|
209
369
|
|
|
210
370
|
result = {
|
|
371
|
+
"initialized": True,
|
|
211
372
|
"entries": len(entries),
|
|
212
373
|
"skills": {
|
|
213
374
|
"total": total_skills,
|
|
@@ -586,7 +747,7 @@ def distill(
|
|
|
586
747
|
"""Extract patterns from all buildlog entries.
|
|
587
748
|
|
|
588
749
|
Parses the Improvements section of each buildlog entry and aggregates
|
|
589
|
-
insights into structured output (JSON or YAML).
|
|
750
|
+
insights into structured output (JSON or YAML). Returns empty result if not initialized.
|
|
590
751
|
|
|
591
752
|
Examples:
|
|
592
753
|
|
|
@@ -596,11 +757,25 @@ def distill(
|
|
|
596
757
|
buildlog distill --since 2026-01-01 # Filter by date
|
|
597
758
|
buildlog distill --category workflow # Filter by category
|
|
598
759
|
"""
|
|
760
|
+
import json as json_module
|
|
761
|
+
|
|
599
762
|
buildlog_dir = Path("buildlog")
|
|
600
763
|
|
|
764
|
+
# Handle uninitialized state gracefully
|
|
601
765
|
if not buildlog_dir.exists():
|
|
602
|
-
|
|
603
|
-
|
|
766
|
+
empty_result = {
|
|
767
|
+
"initialized": False,
|
|
768
|
+
"patterns": [],
|
|
769
|
+
"statistics": {"total_patterns": 0, "total_entries": 0},
|
|
770
|
+
"message": "buildlog not initialized",
|
|
771
|
+
}
|
|
772
|
+
if fmt == "json":
|
|
773
|
+
click.echo(json_module.dumps(empty_result, indent=2))
|
|
774
|
+
else:
|
|
775
|
+
click.echo(
|
|
776
|
+
"# buildlog not initialized - run 'buildlog init' first\npatterns: []"
|
|
777
|
+
)
|
|
778
|
+
return
|
|
604
779
|
|
|
605
780
|
# Convert datetime to date if provided
|
|
606
781
|
since_date = since.date() if since else None
|
|
@@ -651,6 +826,7 @@ def stats(output_json: bool, detailed: bool, since_date: str | None):
|
|
|
651
826
|
"""Show buildlog statistics and analytics.
|
|
652
827
|
|
|
653
828
|
Provides insights on buildlog usage, coverage, and quality.
|
|
829
|
+
Returns empty stats if not initialized.
|
|
654
830
|
|
|
655
831
|
Examples:
|
|
656
832
|
|
|
@@ -659,11 +835,27 @@ def stats(output_json: bool, detailed: bool, since_date: str | None):
|
|
|
659
835
|
buildlog stats --detailed # Include top sources
|
|
660
836
|
buildlog stats --since 2026-01-01
|
|
661
837
|
"""
|
|
838
|
+
import json as json_module
|
|
839
|
+
|
|
662
840
|
buildlog_dir = Path("buildlog")
|
|
663
841
|
|
|
842
|
+
# Handle uninitialized state gracefully
|
|
664
843
|
if not buildlog_dir.exists():
|
|
665
|
-
|
|
666
|
-
|
|
844
|
+
empty_stats = {
|
|
845
|
+
"initialized": False,
|
|
846
|
+
"total_entries": 0,
|
|
847
|
+
"total_patterns": 0,
|
|
848
|
+
"categories": {},
|
|
849
|
+
"date_range": None,
|
|
850
|
+
"message": "buildlog not initialized",
|
|
851
|
+
}
|
|
852
|
+
if output_json:
|
|
853
|
+
click.echo(json_module.dumps(empty_stats, indent=2))
|
|
854
|
+
else:
|
|
855
|
+
click.echo("buildlog stats")
|
|
856
|
+
click.echo("=" * 40)
|
|
857
|
+
click.echo(" Not initialized. Run 'buildlog init' first.")
|
|
858
|
+
return
|
|
667
859
|
|
|
668
860
|
# Parse since date if provided
|
|
669
861
|
parsed_since = None
|
|
@@ -726,7 +918,7 @@ def skills(
|
|
|
726
918
|
"""Generate agent-consumable skills from buildlog patterns.
|
|
727
919
|
|
|
728
920
|
Transforms distilled patterns into actionable rules with deduplication,
|
|
729
|
-
confidence scoring, and stable IDs.
|
|
921
|
+
confidence scoring, and stable IDs. Returns empty set if not initialized.
|
|
730
922
|
|
|
731
923
|
Examples:
|
|
732
924
|
|
|
@@ -743,9 +935,31 @@ def skills(
|
|
|
743
935
|
"""
|
|
744
936
|
buildlog_dir = Path("buildlog")
|
|
745
937
|
|
|
938
|
+
# Handle uninitialized state gracefully - return empty skill set
|
|
746
939
|
if not buildlog_dir.exists():
|
|
747
|
-
|
|
748
|
-
|
|
940
|
+
if fmt == "json":
|
|
941
|
+
import json as json_module
|
|
942
|
+
|
|
943
|
+
click.echo(
|
|
944
|
+
json_module.dumps(
|
|
945
|
+
{
|
|
946
|
+
"initialized": False,
|
|
947
|
+
"skills": {},
|
|
948
|
+
"total_skills": 0,
|
|
949
|
+
"message": "buildlog not initialized",
|
|
950
|
+
},
|
|
951
|
+
indent=2,
|
|
952
|
+
)
|
|
953
|
+
)
|
|
954
|
+
elif fmt == "yaml":
|
|
955
|
+
click.echo(
|
|
956
|
+
"# buildlog not initialized - run 'buildlog init' first\nskills: {}\ntotal_skills: 0"
|
|
957
|
+
)
|
|
958
|
+
else:
|
|
959
|
+
click.echo(
|
|
960
|
+
"No buildlog/ directory found. Run 'buildlog init' to extract skills."
|
|
961
|
+
)
|
|
962
|
+
return
|
|
749
963
|
|
|
750
964
|
# Convert datetime to date if provided
|
|
751
965
|
since_date = since.date() if since else None
|
|
@@ -935,7 +1149,7 @@ def status_cmd(min_confidence: str, output_json: bool):
|
|
|
935
1149
|
"""Show extracted skills by category and confidence.
|
|
936
1150
|
|
|
937
1151
|
Displays all skills extracted from buildlog entries, grouped by category,
|
|
938
|
-
with confidence levels and promotion status.
|
|
1152
|
+
with confidence levels and promotion status. Returns empty state if not initialized.
|
|
939
1153
|
|
|
940
1154
|
Examples:
|
|
941
1155
|
|
|
@@ -948,9 +1162,23 @@ def status_cmd(min_confidence: str, output_json: bool):
|
|
|
948
1162
|
|
|
949
1163
|
buildlog_dir = Path("buildlog")
|
|
950
1164
|
|
|
1165
|
+
# Handle uninitialized state gracefully
|
|
951
1166
|
if not buildlog_dir.exists():
|
|
952
|
-
|
|
953
|
-
|
|
1167
|
+
empty_result = {
|
|
1168
|
+
"initialized": False,
|
|
1169
|
+
"total_skills": 0,
|
|
1170
|
+
"total_entries": 0,
|
|
1171
|
+
"skills": {},
|
|
1172
|
+
"by_confidence": {"high": 0, "medium": 0, "low": 0},
|
|
1173
|
+
"promotable_ids": [],
|
|
1174
|
+
"error": None,
|
|
1175
|
+
}
|
|
1176
|
+
if output_json:
|
|
1177
|
+
click.echo(json_module.dumps(empty_result, indent=2))
|
|
1178
|
+
else:
|
|
1179
|
+
click.echo("Skills: 0 total from 0 entries")
|
|
1180
|
+
click.echo(" buildlog not initialized. Run 'buildlog init' first.")
|
|
1181
|
+
return
|
|
954
1182
|
|
|
955
1183
|
result = status(buildlog_dir, min_confidence=min_confidence) # type: ignore[arg-type]
|
|
956
1184
|
|
|
@@ -1092,6 +1320,7 @@ def diff_cmd(output_json: bool):
|
|
|
1092
1320
|
"""Show skills pending review (not yet promoted or rejected).
|
|
1093
1321
|
|
|
1094
1322
|
Useful for seeing what's new since the last time you reviewed skills.
|
|
1323
|
+
Returns empty diff if not initialized.
|
|
1095
1324
|
|
|
1096
1325
|
Examples:
|
|
1097
1326
|
|
|
@@ -1103,9 +1332,22 @@ def diff_cmd(output_json: bool):
|
|
|
1103
1332
|
|
|
1104
1333
|
buildlog_dir = Path("buildlog")
|
|
1105
1334
|
|
|
1335
|
+
# Handle uninitialized state gracefully
|
|
1106
1336
|
if not buildlog_dir.exists():
|
|
1107
|
-
|
|
1108
|
-
|
|
1337
|
+
empty_result = {
|
|
1338
|
+
"initialized": False,
|
|
1339
|
+
"pending": {},
|
|
1340
|
+
"total_pending": 0,
|
|
1341
|
+
"already_promoted": 0,
|
|
1342
|
+
"already_rejected": 0,
|
|
1343
|
+
"error": None,
|
|
1344
|
+
}
|
|
1345
|
+
if output_json:
|
|
1346
|
+
click.echo(json_module.dumps(empty_result, indent=2))
|
|
1347
|
+
else:
|
|
1348
|
+
click.echo("Pending: 0 | Promoted: 0 | Rejected: 0")
|
|
1349
|
+
click.echo(" buildlog not initialized. Run 'buildlog init' first.")
|
|
1350
|
+
return
|
|
1109
1351
|
|
|
1110
1352
|
result = core_diff(buildlog_dir)
|
|
1111
1353
|
|