buildlog 0.1.0__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.
- buildlog/__init__.py +3 -0
- buildlog/cli.py +437 -0
- buildlog/core/__init__.py +25 -0
- buildlog/core/operations.py +392 -0
- buildlog/distill.py +374 -0
- buildlog/embeddings.py +392 -0
- buildlog/mcp/__init__.py +15 -0
- buildlog/mcp/server.py +29 -0
- buildlog/mcp/tools.py +97 -0
- buildlog/render/__init__.py +41 -0
- buildlog/render/base.py +23 -0
- buildlog/render/claude_md.py +106 -0
- buildlog/render/settings_json.py +96 -0
- buildlog/skills.py +630 -0
- buildlog/stats.py +469 -0
- buildlog-0.1.0.data/data/share/buildlog/copier.yml +35 -0
- buildlog-0.1.0.data/data/share/buildlog/post_gen.py +51 -0
- buildlog-0.1.0.data/data/share/buildlog/template/buildlog/.gitkeep +0 -0
- buildlog-0.1.0.data/data/share/buildlog/template/buildlog/2026-01-01-example.md +269 -0
- buildlog-0.1.0.data/data/share/buildlog/template/buildlog/BUILDLOG_SYSTEM.md +114 -0
- buildlog-0.1.0.data/data/share/buildlog/template/buildlog/_TEMPLATE.md +162 -0
- buildlog-0.1.0.data/data/share/buildlog/template/buildlog/assets/.gitkeep +0 -0
- buildlog-0.1.0.dist-info/METADATA +664 -0
- buildlog-0.1.0.dist-info/RECORD +27 -0
- buildlog-0.1.0.dist-info/WHEEL +4 -0
- buildlog-0.1.0.dist-info/entry_points.txt +3 -0
- buildlog-0.1.0.dist-info/licenses/LICENSE +21 -0
buildlog/__init__.py
ADDED
buildlog/cli.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""CLI for buildlog - engineering notebook for AI-assisted development."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import date, datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from buildlog.distill import CATEGORIES, distill_all, format_output
|
|
12
|
+
from buildlog.skills import generate_skills, format_skills
|
|
13
|
+
from buildlog.stats import calculate_stats, format_dashboard, format_json
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_template_dir() -> Path | None:
|
|
17
|
+
"""Get the template directory from package data.
|
|
18
|
+
|
|
19
|
+
Returns the directory containing copier.yml, or None to fall back to GitHub.
|
|
20
|
+
"""
|
|
21
|
+
# 1. Check if we're in development (template dir exists relative to package)
|
|
22
|
+
# src/buildlog/cli.py -> src/buildlog -> src -> project root
|
|
23
|
+
pkg_dir = Path(__file__).parent.parent.parent
|
|
24
|
+
dev_copier = pkg_dir / "copier.yml"
|
|
25
|
+
if dev_copier.exists():
|
|
26
|
+
return pkg_dir
|
|
27
|
+
|
|
28
|
+
# 2. Check installed location (site-packages/../share/buildlog)
|
|
29
|
+
import sysconfig
|
|
30
|
+
data_dir = Path(sysconfig.get_path("data")) / "share" / "buildlog"
|
|
31
|
+
if (data_dir / "copier.yml").exists():
|
|
32
|
+
return data_dir
|
|
33
|
+
|
|
34
|
+
# 3. Fall back to using copier directly from GitHub
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@click.group()
|
|
39
|
+
@click.version_option()
|
|
40
|
+
def main():
|
|
41
|
+
"""buildlog - Engineering notebook for AI-assisted development.
|
|
42
|
+
|
|
43
|
+
Capture your work as publishable content. Include the fuckups.
|
|
44
|
+
"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@main.command()
|
|
49
|
+
@click.option("--no-claude-md", is_flag=True, help="Don't update CLAUDE.md")
|
|
50
|
+
def init(no_claude_md: bool):
|
|
51
|
+
"""Initialize buildlog in the current directory.
|
|
52
|
+
|
|
53
|
+
Sets up the buildlog/ directory with templates and optionally
|
|
54
|
+
adds instructions to CLAUDE.md.
|
|
55
|
+
"""
|
|
56
|
+
buildlog_dir = Path("buildlog")
|
|
57
|
+
|
|
58
|
+
if buildlog_dir.exists():
|
|
59
|
+
click.echo("buildlog/ directory already exists.", err=True)
|
|
60
|
+
raise SystemExit(1)
|
|
61
|
+
|
|
62
|
+
template_dir = get_template_dir()
|
|
63
|
+
|
|
64
|
+
if template_dir:
|
|
65
|
+
# Use local template
|
|
66
|
+
click.echo("Initializing buildlog from local template...")
|
|
67
|
+
try:
|
|
68
|
+
subprocess.run(
|
|
69
|
+
[
|
|
70
|
+
sys.executable, "-m", "copier", "copy",
|
|
71
|
+
"--trust",
|
|
72
|
+
*(["--data", "update_claude_md=false"] if no_claude_md else []),
|
|
73
|
+
str(template_dir),
|
|
74
|
+
"."
|
|
75
|
+
],
|
|
76
|
+
check=True
|
|
77
|
+
)
|
|
78
|
+
except subprocess.CalledProcessError:
|
|
79
|
+
click.echo("Failed to initialize buildlog.", err=True)
|
|
80
|
+
raise SystemExit(1)
|
|
81
|
+
else:
|
|
82
|
+
# Fall back to GitHub
|
|
83
|
+
click.echo("Initializing buildlog from GitHub...")
|
|
84
|
+
try:
|
|
85
|
+
subprocess.run(
|
|
86
|
+
[
|
|
87
|
+
sys.executable, "-m", "copier", "copy",
|
|
88
|
+
"--trust",
|
|
89
|
+
*(["--data", "update_claude_md=false"] if no_claude_md else []),
|
|
90
|
+
"gh:Peleke/buildlog-template",
|
|
91
|
+
"."
|
|
92
|
+
],
|
|
93
|
+
check=True
|
|
94
|
+
)
|
|
95
|
+
except subprocess.CalledProcessError:
|
|
96
|
+
click.echo("Failed to initialize buildlog.", err=True)
|
|
97
|
+
raise SystemExit(1)
|
|
98
|
+
|
|
99
|
+
click.echo("\n✓ buildlog initialized!")
|
|
100
|
+
click.echo("\nNext: buildlog new my-feature")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@main.command()
|
|
104
|
+
@click.argument("slug")
|
|
105
|
+
@click.option("--date", "-d", "entry_date", default=None, help="Date for entry (YYYY-MM-DD)")
|
|
106
|
+
def new(slug: str, entry_date: str | None):
|
|
107
|
+
"""Create a new buildlog entry.
|
|
108
|
+
|
|
109
|
+
SLUG is a short identifier for the entry (e.g., 'auth-api', 'bugfix-login').
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
|
|
113
|
+
buildlog new auth-api
|
|
114
|
+
buildlog new runpod-deploy --date 2026-01-15
|
|
115
|
+
"""
|
|
116
|
+
buildlog_dir = Path("buildlog")
|
|
117
|
+
template_file = buildlog_dir / "_TEMPLATE.md"
|
|
118
|
+
|
|
119
|
+
if not buildlog_dir.exists():
|
|
120
|
+
click.echo("No buildlog/ directory found. Run 'buildlog init' first.", err=True)
|
|
121
|
+
raise SystemExit(1)
|
|
122
|
+
|
|
123
|
+
if not template_file.exists():
|
|
124
|
+
click.echo("No _TEMPLATE.md found in buildlog/. Run 'buildlog init' first.", err=True)
|
|
125
|
+
raise SystemExit(1)
|
|
126
|
+
|
|
127
|
+
# Determine date
|
|
128
|
+
if entry_date:
|
|
129
|
+
try:
|
|
130
|
+
# Validate date format
|
|
131
|
+
year, month, day = entry_date.split("-")
|
|
132
|
+
date_str = f"{int(year):04d}-{int(month):02d}-{int(day):02d}"
|
|
133
|
+
except ValueError:
|
|
134
|
+
click.echo("Invalid date format. Use YYYY-MM-DD.", err=True)
|
|
135
|
+
raise SystemExit(1)
|
|
136
|
+
else:
|
|
137
|
+
date_str = date.today().isoformat()
|
|
138
|
+
|
|
139
|
+
# Sanitize slug
|
|
140
|
+
safe_slug = slug.lower().replace(" ", "-").replace("_", "-")
|
|
141
|
+
safe_slug = "".join(c for c in safe_slug if c.isalnum() or c == "-")
|
|
142
|
+
|
|
143
|
+
# Create entry
|
|
144
|
+
entry_name = f"{date_str}-{safe_slug}.md"
|
|
145
|
+
entry_path = buildlog_dir / entry_name
|
|
146
|
+
|
|
147
|
+
if entry_path.exists():
|
|
148
|
+
click.echo(f"Entry already exists: {entry_path}", err=True)
|
|
149
|
+
raise SystemExit(1)
|
|
150
|
+
|
|
151
|
+
# Copy template
|
|
152
|
+
shutil.copy(template_file, entry_path)
|
|
153
|
+
|
|
154
|
+
# Replace placeholder date in the new file
|
|
155
|
+
content = entry_path.read_text()
|
|
156
|
+
content = content.replace("[YYYY-MM-DD]", date_str)
|
|
157
|
+
entry_path.write_text(content)
|
|
158
|
+
|
|
159
|
+
click.echo(f"✓ Created {entry_path}")
|
|
160
|
+
click.echo(f"\nOpen it: $EDITOR {entry_path}")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@main.command()
|
|
164
|
+
def list():
|
|
165
|
+
"""List all buildlog entries."""
|
|
166
|
+
buildlog_dir = Path("buildlog")
|
|
167
|
+
|
|
168
|
+
if not buildlog_dir.exists():
|
|
169
|
+
click.echo("No buildlog/ directory found. Run 'buildlog init' first.", err=True)
|
|
170
|
+
raise SystemExit(1)
|
|
171
|
+
|
|
172
|
+
entries = sorted(
|
|
173
|
+
buildlog_dir.glob("20??-??-??-*.md"),
|
|
174
|
+
reverse=True # Most recent first
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if not entries:
|
|
178
|
+
click.echo("No entries yet. Create one with: buildlog new my-feature")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
click.echo(f"Found {len(entries)} entries:\n")
|
|
182
|
+
for entry in entries:
|
|
183
|
+
# Extract title from first line if possible
|
|
184
|
+
try:
|
|
185
|
+
first_line = entry.read_text().split("\n")[0]
|
|
186
|
+
title = first_line.replace("# Build Journal: ", "").replace("# ", "").strip()
|
|
187
|
+
if title == "[TITLE]":
|
|
188
|
+
title = "(untitled)"
|
|
189
|
+
except Exception:
|
|
190
|
+
title = "(unreadable)"
|
|
191
|
+
|
|
192
|
+
click.echo(f" {entry.name}")
|
|
193
|
+
click.echo(f" {title}\n")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@main.command()
|
|
197
|
+
def update():
|
|
198
|
+
"""Update buildlog templates to latest version."""
|
|
199
|
+
template_dir = get_template_dir()
|
|
200
|
+
|
|
201
|
+
if template_dir:
|
|
202
|
+
click.echo("Updating from local template...")
|
|
203
|
+
try:
|
|
204
|
+
subprocess.run(
|
|
205
|
+
[sys.executable, "-m", "copier", "update", "--trust"],
|
|
206
|
+
check=True
|
|
207
|
+
)
|
|
208
|
+
except subprocess.CalledProcessError:
|
|
209
|
+
click.echo("Failed to update. Try running 'copier update' directly.", err=True)
|
|
210
|
+
raise SystemExit(1)
|
|
211
|
+
else:
|
|
212
|
+
click.echo("Updating from GitHub...")
|
|
213
|
+
try:
|
|
214
|
+
subprocess.run(
|
|
215
|
+
[sys.executable, "-m", "copier", "update", "--trust"],
|
|
216
|
+
check=True
|
|
217
|
+
)
|
|
218
|
+
except subprocess.CalledProcessError:
|
|
219
|
+
click.echo("Failed to update. Try running 'copier update' directly.", err=True)
|
|
220
|
+
raise SystemExit(1)
|
|
221
|
+
|
|
222
|
+
click.echo("\n✓ buildlog updated!")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@main.command()
|
|
226
|
+
@click.option("--output", "-o", type=click.Path(), help="Output file (default: stdout)")
|
|
227
|
+
@click.option(
|
|
228
|
+
"--format",
|
|
229
|
+
"fmt",
|
|
230
|
+
type=click.Choice(["json", "yaml"]),
|
|
231
|
+
default="json",
|
|
232
|
+
help="Output format",
|
|
233
|
+
)
|
|
234
|
+
@click.option(
|
|
235
|
+
"--since",
|
|
236
|
+
type=click.DateTime(formats=["%Y-%m-%d"]),
|
|
237
|
+
help="Only include entries from this date onward (YYYY-MM-DD)",
|
|
238
|
+
)
|
|
239
|
+
@click.option(
|
|
240
|
+
"--category",
|
|
241
|
+
type=click.Choice(CATEGORIES),
|
|
242
|
+
help="Filter to a specific category",
|
|
243
|
+
)
|
|
244
|
+
def distill(output: str | None, fmt: str, since: datetime | None, category: str | None):
|
|
245
|
+
"""Extract patterns from all buildlog entries.
|
|
246
|
+
|
|
247
|
+
Parses the Improvements section of each buildlog entry and aggregates
|
|
248
|
+
insights into structured output (JSON or YAML).
|
|
249
|
+
|
|
250
|
+
Examples:
|
|
251
|
+
|
|
252
|
+
buildlog distill # JSON to stdout
|
|
253
|
+
buildlog distill -o patterns.json # Write to file
|
|
254
|
+
buildlog distill --format yaml # YAML output
|
|
255
|
+
buildlog distill --since 2026-01-01 # Filter by date
|
|
256
|
+
buildlog distill --category workflow # Filter by category
|
|
257
|
+
"""
|
|
258
|
+
buildlog_dir = Path("buildlog")
|
|
259
|
+
|
|
260
|
+
if not buildlog_dir.exists():
|
|
261
|
+
click.echo("No buildlog/ directory found. Run 'buildlog init' first.", err=True)
|
|
262
|
+
raise SystemExit(1)
|
|
263
|
+
|
|
264
|
+
# Convert datetime to date if provided
|
|
265
|
+
since_date = since.date() if since else None
|
|
266
|
+
|
|
267
|
+
# Run distillation
|
|
268
|
+
try:
|
|
269
|
+
result = distill_all(buildlog_dir, since=since_date, category_filter=category)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
click.echo(f"Failed to distill entries: {e}", err=True)
|
|
272
|
+
raise SystemExit(1)
|
|
273
|
+
|
|
274
|
+
# Format output
|
|
275
|
+
try:
|
|
276
|
+
formatted = format_output(result, fmt)
|
|
277
|
+
except ImportError as e:
|
|
278
|
+
click.echo(str(e), err=True)
|
|
279
|
+
raise SystemExit(1)
|
|
280
|
+
|
|
281
|
+
# Write output
|
|
282
|
+
if output:
|
|
283
|
+
output_path = Path(output)
|
|
284
|
+
try:
|
|
285
|
+
output_path.write_text(formatted, encoding="utf-8")
|
|
286
|
+
click.echo(f"Wrote {result.statistics.get('total_patterns', 0)} patterns to {output_path}")
|
|
287
|
+
except Exception as e:
|
|
288
|
+
click.echo(f"Failed to write output: {e}", err=True)
|
|
289
|
+
raise SystemExit(1)
|
|
290
|
+
else:
|
|
291
|
+
click.echo(formatted)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@main.command()
|
|
295
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
296
|
+
@click.option("--detailed", is_flag=True, help="Show detailed breakdown including top sources")
|
|
297
|
+
@click.option("--since", "since_date", default=None, help="Only include entries since date (YYYY-MM-DD)")
|
|
298
|
+
def stats(output_json: bool, detailed: bool, since_date: str | None):
|
|
299
|
+
"""Show buildlog statistics and analytics.
|
|
300
|
+
|
|
301
|
+
Provides insights on buildlog usage, coverage, and quality.
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
|
|
305
|
+
buildlog stats # Terminal dashboard
|
|
306
|
+
buildlog stats --json # JSON output for scripts
|
|
307
|
+
buildlog stats --detailed # Include top sources
|
|
308
|
+
buildlog stats --since 2026-01-01
|
|
309
|
+
"""
|
|
310
|
+
buildlog_dir = Path("buildlog")
|
|
311
|
+
|
|
312
|
+
if not buildlog_dir.exists():
|
|
313
|
+
click.echo("No buildlog/ directory found. Run 'buildlog init' first.", err=True)
|
|
314
|
+
raise SystemExit(1)
|
|
315
|
+
|
|
316
|
+
# Parse since date if provided
|
|
317
|
+
parsed_since = None
|
|
318
|
+
if since_date:
|
|
319
|
+
try:
|
|
320
|
+
parsed_since = datetime.strptime(since_date, "%Y-%m-%d").date()
|
|
321
|
+
except ValueError:
|
|
322
|
+
click.echo("Invalid date format. Use YYYY-MM-DD.", err=True)
|
|
323
|
+
raise SystemExit(1)
|
|
324
|
+
|
|
325
|
+
# Calculate stats
|
|
326
|
+
stats_data = calculate_stats(buildlog_dir, since_date=parsed_since)
|
|
327
|
+
|
|
328
|
+
# Output in requested format
|
|
329
|
+
if output_json:
|
|
330
|
+
click.echo(format_json(stats_data))
|
|
331
|
+
else:
|
|
332
|
+
click.echo(format_dashboard(stats_data, detailed=detailed))
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@main.command()
|
|
336
|
+
@click.option("--output", "-o", type=click.Path(), help="Output file (default: stdout)")
|
|
337
|
+
@click.option(
|
|
338
|
+
"--format",
|
|
339
|
+
"fmt",
|
|
340
|
+
type=click.Choice(["yaml", "json", "markdown", "rules", "settings"]),
|
|
341
|
+
default="yaml",
|
|
342
|
+
help="Output format: yaml, json, markdown, rules (CLAUDE.md), settings (.claude/settings.json)",
|
|
343
|
+
)
|
|
344
|
+
@click.option(
|
|
345
|
+
"--min-frequency",
|
|
346
|
+
type=int,
|
|
347
|
+
default=1,
|
|
348
|
+
help="Only include skills seen at least this many times",
|
|
349
|
+
)
|
|
350
|
+
@click.option(
|
|
351
|
+
"--since",
|
|
352
|
+
type=click.DateTime(formats=["%Y-%m-%d"]),
|
|
353
|
+
help="Only include entries from this date onward (YYYY-MM-DD)",
|
|
354
|
+
)
|
|
355
|
+
@click.option(
|
|
356
|
+
"--embeddings",
|
|
357
|
+
type=click.Choice(["token", "sentence-transformers", "openai"]),
|
|
358
|
+
default=None,
|
|
359
|
+
help="Embedding backend for semantic deduplication",
|
|
360
|
+
)
|
|
361
|
+
def skills(
|
|
362
|
+
output: str | None,
|
|
363
|
+
fmt: str,
|
|
364
|
+
min_frequency: int,
|
|
365
|
+
since: datetime | None,
|
|
366
|
+
embeddings: str | None,
|
|
367
|
+
):
|
|
368
|
+
"""Generate agent-consumable skills from buildlog patterns.
|
|
369
|
+
|
|
370
|
+
Transforms distilled patterns into actionable rules with deduplication,
|
|
371
|
+
confidence scoring, and stable IDs.
|
|
372
|
+
|
|
373
|
+
Examples:
|
|
374
|
+
|
|
375
|
+
buildlog skills # YAML to stdout
|
|
376
|
+
buildlog skills -o skills.yml # Write to file
|
|
377
|
+
buildlog skills --format markdown # For CLAUDE.md injection
|
|
378
|
+
buildlog skills --min-frequency 2 # Only repeated patterns
|
|
379
|
+
buildlog skills --embeddings sentence-transformers # Semantic dedup
|
|
380
|
+
|
|
381
|
+
Embedding backends:
|
|
382
|
+
token (default): Fast, no dependencies, token-based similarity
|
|
383
|
+
sentence-transformers: Local semantic embeddings (pip install buildlog[embeddings])
|
|
384
|
+
openai: OpenAI API embeddings (requires OPENAI_API_KEY)
|
|
385
|
+
"""
|
|
386
|
+
buildlog_dir = Path("buildlog")
|
|
387
|
+
|
|
388
|
+
if not buildlog_dir.exists():
|
|
389
|
+
click.echo("No buildlog/ directory found. Run 'buildlog init' first.", err=True)
|
|
390
|
+
raise SystemExit(1)
|
|
391
|
+
|
|
392
|
+
# Convert datetime to date if provided
|
|
393
|
+
since_date = since.date() if since else None
|
|
394
|
+
|
|
395
|
+
# Generate skills
|
|
396
|
+
try:
|
|
397
|
+
skill_set = generate_skills(
|
|
398
|
+
buildlog_dir,
|
|
399
|
+
min_frequency=min_frequency,
|
|
400
|
+
since_date=since_date,
|
|
401
|
+
embedding_backend=embeddings,
|
|
402
|
+
)
|
|
403
|
+
except ImportError as e:
|
|
404
|
+
click.echo(f"Missing dependency: {e}", err=True)
|
|
405
|
+
raise SystemExit(1)
|
|
406
|
+
except Exception as e:
|
|
407
|
+
click.echo(f"Failed to generate skills: {e}", err=True)
|
|
408
|
+
raise SystemExit(1)
|
|
409
|
+
|
|
410
|
+
# Format output
|
|
411
|
+
try:
|
|
412
|
+
formatted = format_skills(skill_set, fmt)
|
|
413
|
+
except ImportError as e:
|
|
414
|
+
click.echo(str(e), err=True)
|
|
415
|
+
raise SystemExit(1)
|
|
416
|
+
except ValueError as e:
|
|
417
|
+
click.echo(str(e), err=True)
|
|
418
|
+
raise SystemExit(1)
|
|
419
|
+
|
|
420
|
+
# Write output
|
|
421
|
+
if output:
|
|
422
|
+
output_path = Path(output)
|
|
423
|
+
try:
|
|
424
|
+
output_path.write_text(formatted, encoding="utf-8")
|
|
425
|
+
click.echo(
|
|
426
|
+
f"Wrote {skill_set.total_skills} skills to {output_path} "
|
|
427
|
+
f"(from {skill_set.source_entries} entries)"
|
|
428
|
+
)
|
|
429
|
+
except Exception as e:
|
|
430
|
+
click.echo(f"Failed to write output: {e}", err=True)
|
|
431
|
+
raise SystemExit(1)
|
|
432
|
+
else:
|
|
433
|
+
click.echo(formatted)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
if __name__ == "__main__":
|
|
437
|
+
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Core operations for buildlog skill management."""
|
|
2
|
+
|
|
3
|
+
from buildlog.core.operations import (
|
|
4
|
+
DiffResult,
|
|
5
|
+
PromoteResult,
|
|
6
|
+
RejectResult,
|
|
7
|
+
StatusResult,
|
|
8
|
+
diff,
|
|
9
|
+
find_skills_by_ids,
|
|
10
|
+
promote,
|
|
11
|
+
reject,
|
|
12
|
+
status,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"StatusResult",
|
|
17
|
+
"PromoteResult",
|
|
18
|
+
"RejectResult",
|
|
19
|
+
"DiffResult",
|
|
20
|
+
"status",
|
|
21
|
+
"promote",
|
|
22
|
+
"reject",
|
|
23
|
+
"diff",
|
|
24
|
+
"find_skills_by_ids",
|
|
25
|
+
]
|