ragtime-cli 0.2.1__tar.gz → 0.2.3__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.
Files changed (28) hide show
  1. {ragtime_cli-0.2.1/ragtime_cli.egg-info → ragtime_cli-0.2.3}/PKG-INFO +1 -1
  2. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/pyproject.toml +1 -1
  3. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3/ragtime_cli.egg-info}/PKG-INFO +1 -1
  4. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/ragtime_cli.egg-info/SOURCES.txt +2 -0
  5. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/cli.py +264 -7
  6. ragtime_cli-0.2.3/src/commands/create-pr.md +389 -0
  7. ragtime_cli-0.2.3/src/commands/import-docs.md +259 -0
  8. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/pr-graduate.md +7 -2
  9. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/config.py +19 -0
  10. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/LICENSE +0 -0
  11. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/README.md +0 -0
  12. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/ragtime_cli.egg-info/dependency_links.txt +0 -0
  13. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/ragtime_cli.egg-info/entry_points.txt +0 -0
  14. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/ragtime_cli.egg-info/requires.txt +0 -0
  15. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/ragtime_cli.egg-info/top_level.txt +0 -0
  16. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/setup.cfg +0 -0
  17. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/__init__.py +0 -0
  18. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/audit.md +0 -0
  19. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/handoff.md +0 -0
  20. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/recall.md +0 -0
  21. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/remember.md +0 -0
  22. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/save.md +0 -0
  23. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/commands/start.md +0 -0
  24. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/db.py +0 -0
  25. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/indexers/__init__.py +0 -0
  26. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/indexers/docs.py +0 -0
  27. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/mcp_server.py +0 -0
  28. {ragtime_cli-0.2.1 → ragtime_cli-0.2.3}/src/memory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragtime-cli
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Local-first memory and RAG system for Claude Code - semantic search over code, docs, and team knowledge
5
5
  Author-email: Bret Martineau <bretwardjames@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ragtime-cli"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  description = "Local-first memory and RAG system for Claude Code - semantic search over code, docs, and team knowledge"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragtime-cli
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Local-first memory and RAG system for Claude Code - semantic search over code, docs, and team knowledge
5
5
  Author-email: Bret Martineau <bretwardjames@gmail.com>
6
6
  License-Expression: MIT
@@ -14,7 +14,9 @@ src/db.py
14
14
  src/mcp_server.py
15
15
  src/memory.py
16
16
  src/commands/audit.md
17
+ src/commands/create-pr.md
17
18
  src/commands/handoff.md
19
+ src/commands/import-docs.md
18
20
  src/commands/pr-graduate.md
19
21
  src/commands/recall.md
20
22
  src/commands/remember.md
@@ -166,7 +166,7 @@ def get_remote_branches_with_ragtime(path: Path) -> list[str]:
166
166
 
167
167
 
168
168
  @click.group()
169
- @click.version_option(version="0.2.1")
169
+ @click.version_option(version="0.2.3")
170
170
  def main():
171
171
  """Ragtime - semantic search over code and documentation."""
172
172
  pass
@@ -206,11 +206,45 @@ index/
206
206
  """
207
207
  gitignore_path.write_text(gitignore_content)
208
208
 
209
+ # Create conventions file template
210
+ conventions_file = ragtime_dir / "CONVENTIONS.md"
211
+ if not conventions_file.exists():
212
+ conventions_file.write_text("""# Team Conventions
213
+
214
+ Rules and patterns that code must follow. These are checked by `/create-pr`.
215
+
216
+ ## Code Style
217
+
218
+ - [ ] Example: Use async/await, not .then() chains
219
+ - [ ] Example: All API endpoints must use auth middleware
220
+
221
+ ## Architecture
222
+
223
+ - [ ] Example: Services should not directly access repositories from other domains
224
+
225
+ ## Security
226
+
227
+ - [ ] Example: Never commit .env or credentials files
228
+ - [ ] Example: All user input must be validated
229
+
230
+ ## Testing
231
+
232
+ - [ ] Example: All new features need unit tests
233
+
234
+ ---
235
+
236
+ Add your team's conventions above. Each rule should be:
237
+ - Clear and specific
238
+ - Checkable against code
239
+ - Actionable (what to do, not just what not to do)
240
+ """)
241
+
209
242
  click.echo(f"\nCreated .ragtime/ structure:")
210
- click.echo(f" app/ - Graduated knowledge (tracked)")
211
- click.echo(f" team/ - Team conventions (tracked)")
212
- click.echo(f" branches/ - Active branches (yours tracked, synced gitignored)")
213
- click.echo(f" archive/ - Completed branches (tracked)")
243
+ click.echo(f" app/ - Graduated knowledge (tracked)")
244
+ click.echo(f" team/ - Team conventions (tracked)")
245
+ click.echo(f" branches/ - Active branches (yours tracked, synced gitignored)")
246
+ click.echo(f" archive/ - Completed branches (tracked)")
247
+ click.echo(f" CONVENTIONS.md - Team rules checked by /create-pr")
214
248
 
215
249
  # Check for ghp-cli
216
250
  if check_ghp_installed():
@@ -362,6 +396,9 @@ def config(path: Path):
362
396
  click.echo(f" Paths: {cfg.code.paths}")
363
397
  click.echo(f" Languages: {cfg.code.languages}")
364
398
  click.echo(f" Exclude: {cfg.code.exclude}")
399
+ click.echo("\nConventions:")
400
+ click.echo(f" Files: {cfg.conventions.files}")
401
+ click.echo(f" Also search memories: {cfg.conventions.also_search_memories}")
365
402
 
366
403
 
367
404
  # ============================================================================
@@ -709,21 +746,54 @@ def install_commands(global_install: bool, workspace_install: bool, list_command
709
746
  target_dir.mkdir(parents=True, exist_ok=True)
710
747
  commands_dir = get_commands_dir()
711
748
  installed = 0
749
+ skipped = 0
750
+ namespaced = 0
712
751
 
713
752
  for cmd in to_install:
714
753
  source = commands_dir / f"{cmd}.md"
715
754
  target = target_dir / f"{cmd}.md"
755
+ namespaced_target = target_dir / f"ragtime-{cmd}.md"
716
756
 
717
757
  if target.exists() and not force:
718
- if click.confirm(f" {cmd}.md exists. Overwrite?", default=False):
758
+ # Check if it's our file (contains ragtime marker)
759
+ existing_content = target.read_text()
760
+ is_ragtime_file = "ragtime" in existing_content.lower() and "mcp__ragtime" in existing_content
761
+
762
+ if is_ragtime_file:
763
+ # It's our file, safe to overwrite
719
764
  target.write_text(source.read_text())
765
+ click.echo(f" ✓ {cmd}.md (updated)")
720
766
  installed += 1
767
+ else:
768
+ # Conflict with non-ragtime command
769
+ click.echo(f"\n⚠️ Conflict: {cmd}.md already exists (not a ragtime command)")
770
+ click.echo(f" 1. Overwrite with ragtime's version")
771
+ click.echo(f" 2. Skip (keep existing)")
772
+ click.echo(f" 3. Install as ragtime-{cmd}.md")
773
+
774
+ choice = click.prompt(" Choice", type=click.Choice(["1", "2", "3"]), default="2")
775
+
776
+ if choice == "1":
777
+ target.write_text(source.read_text())
778
+ click.echo(f" ✓ {cmd}.md (overwritten)")
779
+ installed += 1
780
+ elif choice == "2":
781
+ click.echo(f" • {cmd}.md (skipped)")
782
+ skipped += 1
783
+ else:
784
+ namespaced_target.write_text(source.read_text())
785
+ click.echo(f" ✓ ragtime-{cmd}.md")
786
+ namespaced += 1
721
787
  else:
722
788
  target.write_text(source.read_text())
723
789
  click.echo(f" ✓ {cmd}.md")
724
790
  installed += 1
725
791
 
726
792
  click.echo(f"\nInstalled {installed} commands to {target_dir}")
793
+ if namespaced:
794
+ click.echo(f" ({namespaced} installed with ragtime- prefix)")
795
+ if skipped:
796
+ click.echo(f" ({skipped} skipped due to conflicts)")
727
797
 
728
798
 
729
799
  @main.command("setup-ghp")
@@ -1105,6 +1175,193 @@ def daemon_status(path: Path):
1105
1175
  pid_file.unlink()
1106
1176
 
1107
1177
 
1178
+ @main.command()
1179
+ @click.argument("docs_path", type=click.Path(exists=True, path_type=Path), default="docs")
1180
+ @click.option("--path", type=click.Path(exists=True, path_type=Path), default=".")
1181
+ @click.option("--fix", is_flag=True, help="Interactively add frontmatter to files")
1182
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
1183
+ def audit(docs_path: Path, path: Path, fix: bool, as_json: bool):
1184
+ """Audit docs for ragtime-compatible frontmatter.
1185
+
1186
+ Scans markdown files and suggests metadata for better indexing.
1187
+
1188
+ Examples:
1189
+ ragtime audit docs/ # Audit docs folder
1190
+ ragtime audit docs/ --fix # Interactively add frontmatter
1191
+ ragtime audit . --json # Output suggestions as JSON
1192
+ """
1193
+ import re
1194
+ import json as json_module
1195
+
1196
+ path = Path(path).resolve()
1197
+ docs_path = Path(docs_path).resolve()
1198
+
1199
+ if not docs_path.exists():
1200
+ click.echo(f"✗ Path not found: {docs_path}", err=True)
1201
+ return
1202
+
1203
+ # Find all markdown files
1204
+ md_files = list(docs_path.rglob("*.md"))
1205
+
1206
+ if not md_files:
1207
+ click.echo(f"No markdown files found in {docs_path}")
1208
+ return
1209
+
1210
+ results = []
1211
+
1212
+ for md_file in md_files:
1213
+ content = md_file.read_text()
1214
+ relative = md_file.relative_to(path) if md_file.is_relative_to(path) else md_file
1215
+
1216
+ # Check for existing frontmatter
1217
+ has_frontmatter = content.startswith("---")
1218
+ existing_meta = {}
1219
+
1220
+ if has_frontmatter:
1221
+ try:
1222
+ import yaml
1223
+ parts = content.split("---", 2)
1224
+ if len(parts) >= 3:
1225
+ existing_meta = yaml.safe_load(parts[1]) or {}
1226
+ except:
1227
+ pass
1228
+
1229
+ # Analyze file for suggestions
1230
+ suggestions = analyze_doc_for_metadata(md_file, content, existing_meta)
1231
+
1232
+ status = "ok" if not suggestions["missing"] else "needs_update"
1233
+ if not has_frontmatter:
1234
+ status = "no_frontmatter"
1235
+
1236
+ results.append({
1237
+ "file": str(relative),
1238
+ "status": status,
1239
+ "has_frontmatter": has_frontmatter,
1240
+ "existing": existing_meta,
1241
+ "suggestions": suggestions,
1242
+ })
1243
+
1244
+ if as_json:
1245
+ click.echo(json_module.dumps(results, indent=2))
1246
+ return
1247
+
1248
+ # Summary
1249
+ no_fm = [r for r in results if r["status"] == "no_frontmatter"]
1250
+ needs_update = [r for r in results if r["status"] == "needs_update"]
1251
+ ok = [r for r in results if r["status"] == "ok"]
1252
+
1253
+ click.echo(f"\nAudited {len(md_files)} files in {docs_path.name}/\n")
1254
+
1255
+ if ok:
1256
+ click.echo(f"✓ {len(ok)} files have complete frontmatter")
1257
+
1258
+ if needs_update:
1259
+ click.echo(f"• {len(needs_update)} files could use more metadata")
1260
+
1261
+ if no_fm:
1262
+ click.echo(f"✗ {len(no_fm)} files have no frontmatter\n")
1263
+
1264
+ for r in no_fm[:10]: # Show first 10
1265
+ click.echo(f"{'─' * 60}")
1266
+ click.echo(f" {r['file']}")
1267
+ s = r["suggestions"]
1268
+ click.echo(f" Suggested frontmatter:")
1269
+ click.echo(f" namespace: {s.get('namespace', 'app')}")
1270
+ click.echo(f" type: {s.get('type', 'document')}")
1271
+ if s.get("component"):
1272
+ click.echo(f" component: {s['component']}")
1273
+
1274
+ if len(no_fm) > 10:
1275
+ click.echo(f"\n ... and {len(no_fm) - 10} more files")
1276
+
1277
+ if fix and no_fm:
1278
+ click.echo(f"\n{'─' * 60}")
1279
+ if click.confirm(f"\nAdd frontmatter to {len(no_fm)} files?"):
1280
+ added = 0
1281
+ for r in no_fm:
1282
+ file_path = path / r["file"]
1283
+ content = file_path.read_text()
1284
+ s = r["suggestions"]
1285
+
1286
+ # Build frontmatter
1287
+ fm_lines = ["---"]
1288
+ fm_lines.append(f"namespace: {s.get('namespace', 'app')}")
1289
+ fm_lines.append(f"type: {s.get('type', 'document')}")
1290
+ if s.get("component"):
1291
+ fm_lines.append(f"component: {s['component']}")
1292
+ fm_lines.append("---")
1293
+ fm_lines.append("")
1294
+
1295
+ new_content = "\n".join(fm_lines) + content
1296
+ file_path.write_text(new_content)
1297
+ added += 1
1298
+ click.echo(f" ✓ {r['file']}")
1299
+
1300
+ click.echo(f"\n✓ Added frontmatter to {added} files")
1301
+ click.echo(f" Run 'ragtime reindex' to update the search index")
1302
+
1303
+
1304
+ def analyze_doc_for_metadata(file_path: Path, content: str, existing: dict) -> dict:
1305
+ """Analyze a document and suggest metadata."""
1306
+ import re
1307
+
1308
+ suggestions = {}
1309
+ missing = []
1310
+
1311
+ # Infer from path
1312
+ parts = file_path.parts
1313
+ path_lower = str(file_path).lower()
1314
+
1315
+ # Namespace inference
1316
+ if "namespace" not in existing:
1317
+ missing.append("namespace")
1318
+ if ".ragtime" in path_lower or "memory" in path_lower:
1319
+ suggestions["namespace"] = "app"
1320
+ elif "team" in path_lower or "convention" in path_lower:
1321
+ suggestions["namespace"] = "team"
1322
+ else:
1323
+ suggestions["namespace"] = "app"
1324
+
1325
+ # Type inference
1326
+ if "type" not in existing:
1327
+ missing.append("type")
1328
+
1329
+ # Check content for clues
1330
+ content_lower = content.lower()
1331
+
1332
+ if "api" in path_lower or "endpoint" in content_lower:
1333
+ suggestions["type"] = "architecture"
1334
+ elif "decision" in path_lower or "adr" in path_lower or "we decided" in content_lower:
1335
+ suggestions["type"] = "decision"
1336
+ elif "guide" in path_lower or "how to" in content_lower:
1337
+ suggestions["type"] = "pattern"
1338
+ elif "setup" in path_lower or "install" in path_lower:
1339
+ suggestions["type"] = "convention"
1340
+ elif "readme" in path_lower:
1341
+ suggestions["type"] = "document"
1342
+ elif "changelog" in path_lower or "release" in path_lower:
1343
+ suggestions["type"] = "document"
1344
+ else:
1345
+ suggestions["type"] = "document"
1346
+
1347
+ # Component inference from path
1348
+ if "component" not in existing:
1349
+ # Look for component-like folder names
1350
+ component_candidates = []
1351
+ skip = {"docs", "src", "lib", "app", "pages", "components", "memory", ".ragtime"}
1352
+
1353
+ for part in parts[:-1]: # Exclude filename
1354
+ if part.lower() not in skip and not part.startswith("."):
1355
+ component_candidates.append(part.lower())
1356
+
1357
+ if component_candidates:
1358
+ suggestions["component"] = component_candidates[-1] # Most specific
1359
+ missing.append("component")
1360
+
1361
+ suggestions["missing"] = missing
1362
+ return suggestions
1363
+
1364
+
1108
1365
  @main.command()
1109
1366
  @click.option("--check", is_flag=True, help="Only check for updates, don't install")
1110
1367
  def update(check: bool):
@@ -1113,7 +1370,7 @@ def update(check: bool):
1113
1370
  from urllib.request import urlopen
1114
1371
  from urllib.error import URLError
1115
1372
 
1116
- current = "0.2.1"
1373
+ current = "0.2.3"
1117
1374
 
1118
1375
  click.echo(f"Current version: {current}")
1119
1376
  click.echo("Checking PyPI for updates...")
@@ -0,0 +1,389 @@
1
+ ---
2
+ description: Create a PR with convention checks and graduated knowledge
3
+ allowed-arguments: optional PR title
4
+ allowed-tools: Bash, Read, Write, Edit, AskUserQuestion, mcp__ragtime__search, mcp__ragtime__memories
5
+ ---
6
+
7
+ # Create PR
8
+
9
+ Create a pull request with branch knowledge graduated and committed alongside the code.
10
+
11
+ **Usage:**
12
+ - `/create-pr` - Interactive PR creation with memory curation
13
+ - `/create-pr "Add authentication flow"` - With suggested title
14
+
15
+ ## Overview
16
+
17
+ This command ensures code follows team standards and knowledge gets merged with the code:
18
+
19
+ 1. Check code against team conventions and app patterns
20
+ 2. Review branch memories and decide what graduates to `app/`
21
+ 3. Check for conflicts with existing app knowledge
22
+ 4. Commit graduated memories as part of the PR
23
+ 5. Create the pull request
24
+
25
+ ## Step 1: Gather Context
26
+
27
+ ```bash
28
+ BRANCH=$(git branch --show-current)
29
+ BRANCH_SLUG=$(echo "$BRANCH" | tr '/' '-')
30
+
31
+ echo "Branch: $BRANCH"
32
+
33
+ # Check for uncommitted changes
34
+ git status --short
35
+
36
+ # Get commits on this branch
37
+ git log main..HEAD --oneline
38
+ ```
39
+
40
+ ## Step 2: Check Code Against Team Conventions
41
+
42
+ Before creating the PR, verify the code follows team standards.
43
+
44
+ ### Load conventions config
45
+
46
+ Check `.ragtime/config.yaml` for conventions settings:
47
+
48
+ ```yaml
49
+ # Default config (if not specified):
50
+ conventions:
51
+ files:
52
+ - ".ragtime/CONVENTIONS.md"
53
+ also_search_memories: true
54
+ ```
55
+
56
+ ### Read conventions files
57
+
58
+ For each file in `conventions.files`, read it if it exists:
59
+
60
+ ```bash
61
+ # Check for conventions files
62
+ cat .ragtime/CONVENTIONS.md 2>/dev/null
63
+ # Or other configured files like docs/CODING_STANDARDS.md
64
+ ```
65
+
66
+ The conventions doc should contain clear, checkable rules like:
67
+ - "All API endpoints must use auth middleware"
68
+ - "Use async/await, not .then() chains"
69
+ - "Never commit .env files"
70
+
71
+ ### Also search memories (if enabled)
72
+
73
+ If `also_search_memories: true` (default), also search for convention memories:
74
+
75
+ ```
76
+ mcp__ragtime__search:
77
+ query: "convention standard pattern rule always never"
78
+ namespace: "team"
79
+ limit: 20
80
+ ```
81
+
82
+ ```
83
+ mcp__ragtime__search:
84
+ query: "pattern architecture convention"
85
+ namespace: "app"
86
+ type: "convention,pattern"
87
+ limit: 20
88
+ ```
89
+
90
+ ### Get changed files
91
+
92
+ ```bash
93
+ # Files changed in this branch
94
+ git diff --name-only main...HEAD
95
+ ```
96
+
97
+ ### Review code against conventions
98
+
99
+ For each relevant convention/pattern found, check if the changed code follows it:
100
+
101
+ 1. Read the convention/pattern memory
102
+ 2. Read the relevant changed files
103
+ 3. Check for compliance
104
+
105
+ Present findings:
106
+
107
+ ```
108
+ ───────────────────────────────────────────
109
+ 🔍 CONVENTION CHECK
110
+ ───────────────────────────────────────────
111
+
112
+ Checked {file_count} changed files against {convention_count} team conventions.
113
+
114
+ ✅ PASSING:
115
+ - "Use async/await, not .then()" - All async code uses await
116
+ - "Error responses use ApiError class" - Verified in api/routes.ts
117
+
118
+ ⚠️ POTENTIAL ISSUES:
119
+ - "All API endpoints need auth middleware"
120
+ → src/api/newEndpoint.ts:15 - No auth middleware detected
121
+
122
+ - "Use logger.error(), not console.error()"
123
+ → src/services/auth.ts:42 - Found console.error()
124
+
125
+ ❌ VIOLATIONS:
126
+ - "Never commit .env files"
127
+ → .env.local is staged
128
+ ───────────────────────────────────────────
129
+ ```
130
+
131
+ ### Handle issues
132
+
133
+ If issues found:
134
+
135
+ ```
136
+ Found {issue_count} potential issues.
137
+
138
+ Options:
139
+ 1. **Fix now** - I'll help fix these before creating PR
140
+ 2. **Ignore** - Create PR anyway (add justification comment)
141
+ 3. **Review each** - Go through issues one by one
142
+ 4. **Cancel** - Fix manually and try again
143
+ ```
144
+
145
+ If "Fix now": Help the user fix each issue before proceeding.
146
+
147
+ If "Ignore": Ask for justification to include in PR description.
148
+
149
+ **Only proceed to memory curation after issues are resolved or acknowledged.**
150
+
151
+ ## Step 3: Check for Branch Memories
152
+
153
+ Search for memories in this branch's namespace:
154
+
155
+ ```bash
156
+ # Check .ragtime/branches/{branch-slug}/ for memory files
157
+ ls -la .ragtime/branches/$BRANCH_SLUG/ 2>/dev/null || echo "No branch memories found"
158
+ ```
159
+
160
+ Also search the index:
161
+
162
+ ```
163
+ mcp__ragtime__search:
164
+ query: "*"
165
+ namespace: "branch-{branch-slug}"
166
+ limit: 50
167
+ ```
168
+
169
+ **If no branch memories found:** Skip to Step 8 (Create PR).
170
+
171
+ **If memories found:** Continue to Step 4.
172
+
173
+ ## Step 4: Present Memories for Curation
174
+
175
+ ```
176
+ ───────────────────────────────────────────
177
+ 📋 BRANCH KNOWLEDGE REVIEW
178
+ ───────────────────────────────────────────
179
+
180
+ Before creating the PR, let's review knowledge from this branch.
181
+
182
+ Graduated memories will be:
183
+ - Moved to app/ or team/ namespace
184
+ - Committed as part of this PR
185
+ - Merged with your code changes
186
+
187
+ Found {count} memories in branch-{branch}:
188
+
189
+ ───────────────────────────────────────────
190
+ ```
191
+
192
+ For each memory, show:
193
+
194
+ ```
195
+ **Memory {n} of {count}:**
196
+
197
+ "{content preview - first 150 chars}..."
198
+
199
+ Type: {type} | Component: {component}
200
+ File: {relative_path}
201
+
202
+ Action?
203
+ 1. ✅ Graduate to app/
204
+ 2. 🏛️ Graduate to team/ (conventions/standards)
205
+ 3. 📚 Keep in branch (don't include in PR)
206
+ 4. ❌ Abandon (mark as noise)
207
+ 5. 👀 Show full content
208
+ ```
209
+
210
+ ## Step 5: Check for Conflicts
211
+
212
+ For each memory being graduated, search for similar content in app/:
213
+
214
+ ```
215
+ mcp__ragtime__search:
216
+ query: "{memory content keywords}"
217
+ namespace: "app"
218
+ limit: 5
219
+ ```
220
+
221
+ If similar memories exist (similarity > 0.8), present:
222
+
223
+ ```
224
+ ⚠️ POTENTIAL CONFLICT
225
+
226
+ Graduating: "{new memory preview}..."
227
+
228
+ Similar existing memory in app/:
229
+ "{existing memory preview}..."
230
+ File: {existing_file}
231
+
232
+ Options:
233
+ 1. **Keep both** - They're different enough
234
+ 2. **Replace existing** - New one is better/more current
235
+ 3. **Merge** - Combine into one comprehensive memory
236
+ 4. **Skip graduation** - Existing one is sufficient
237
+ ```
238
+
239
+ ### If merging:
240
+
241
+ Ask the user to provide the merged content, or offer to draft it:
242
+
243
+ ```
244
+ I can draft a merged version combining both. Want me to try?
245
+ ```
246
+
247
+ If yes, present the draft for approval before saving.
248
+
249
+ ## Step 6: Execute Graduation
250
+
251
+ For each memory to graduate:
252
+
253
+ ### Move the file
254
+
255
+ ```bash
256
+ # From: .ragtime/branches/{branch-slug}/{id}-{slug}.md
257
+ # To: .ragtime/app/{component}/{id}-{slug}.md (or .ragtime/team/)
258
+
259
+ mkdir -p .ragtime/app/{component}
260
+ mv .ragtime/branches/{branch-slug}/{id}-{slug}.md .ragtime/app/{component}/
261
+ ```
262
+
263
+ ### Update the frontmatter
264
+
265
+ Edit the file to update:
266
+ - `namespace: app` (or `team`)
267
+ - `confidence: high`
268
+ - `confidence_reason: pr-graduate`
269
+ - `source: pr-graduate`
270
+ - Add `graduated_from: branch-{branch}`
271
+
272
+ ### Stage the changes
273
+
274
+ ```bash
275
+ git add .ragtime/app/
276
+ git add .ragtime/team/
277
+ # Don't stage branch/ folder - it stays local or gets cleaned up later
278
+ ```
279
+
280
+ ## Step 7: Commit Knowledge (if any graduated)
281
+
282
+ If memories were graduated:
283
+
284
+ ```bash
285
+ git add .ragtime/app/ .ragtime/team/
286
+
287
+ git commit -m "docs: graduate branch knowledge to app
288
+
289
+ Graduated {count} memories from branch-{branch}:
290
+ - {memory1 summary}
291
+ - {memory2 summary}
292
+ ..."
293
+ ```
294
+
295
+ ## Step 8: Create the PR
296
+
297
+ ```bash
298
+ # Check if we need to push
299
+ git push -u origin $BRANCH 2>/dev/null || git push
300
+
301
+ # Create PR
302
+ gh pr create --title "{title}" --body "$(cat <<'EOF'
303
+ ## Summary
304
+
305
+ {summary from commits and context}
306
+
307
+ ## Convention Compliance
308
+
309
+ {if all passed}
310
+ ✅ All team conventions verified
311
+
312
+ {if issues were acknowledged}
313
+ ⚠️ Known deviations:
314
+ - {issue}: {justification}
315
+
316
+ ## Knowledge Added
317
+
318
+ {if memories were graduated}
319
+ This PR includes {count} graduated memories:
320
+ - {list of graduated memories with types}
321
+
322
+ ## Test Plan
323
+
324
+ - [ ] {test items}
325
+
326
+ ---
327
+ 🤖 Generated with [Claude Code](https://claude.ai/code)
328
+ EOF
329
+ )"
330
+ ```
331
+
332
+ ## Step 9: Summary
333
+
334
+ ```
335
+ ───────────────────────────────────────────
336
+ ✅ PR CREATED
337
+ ───────────────────────────────────────────
338
+
339
+ PR: {url}
340
+ Branch: {branch}
341
+
342
+ Knowledge graduated: {count}
343
+ → app/: {app_count}
344
+ → team/: {team_count}
345
+
346
+ Remaining in branch/: {kept_count}
347
+ Abandoned: {abandoned_count}
348
+
349
+ The graduated knowledge is now part of your PR.
350
+ Reviewers will see it alongside your code changes.
351
+ ```
352
+
353
+ ## Quick Mode
354
+
355
+ For branches with few memories, offer quick mode:
356
+
357
+ ```
358
+ Found {count} branch memories.
359
+
360
+ 1. **Review each** - Curate one by one
361
+ 2. **Quick mode** - I'll propose what to graduate
362
+ 3. **Graduate all to app/** - Promote everything
363
+ 4. **Skip knowledge** - Create PR without graduating
364
+ ```
365
+
366
+ Quick mode analyzes content and proposes:
367
+
368
+ ```
369
+ ## Quick Mode Proposal
370
+
371
+ **Graduate to app/:**
372
+ - "Auth uses JWT with 15-min expiry" (architecture)
373
+ - "Redis chosen for session storage" (decision)
374
+
375
+ **Keep in branch:**
376
+ - "Debug notes: token refresh" (task-state)
377
+
378
+ **Abandon:**
379
+ - "TODO: fix later" (noise)
380
+
381
+ Approve this? (yes / review each / edit)
382
+ ```
383
+
384
+ ## Notes
385
+
386
+ - Graduated memories are committed as part of the PR, so reviewers see them
387
+ - Branch memories that aren't graduated stay in `.ragtime/branches/` for reference
388
+ - After PR merges, run `ragtime prune` to clean up synced branch folders
389
+ - The old `/pr-graduate` command can still be used if you forget to graduate before PR
@@ -0,0 +1,259 @@
1
+ ---
2
+ description: Migrate existing docs into ragtime memory structure with AI-assisted classification
3
+ allowed-arguments: path to docs folder (e.g., /import-docs docs/)
4
+ allowed-tools: Bash, Read, Write, Edit, AskUserQuestion
5
+ ---
6
+
7
+ # Import Docs
8
+
9
+ Analyze an existing docs folder and migrate content into the ragtime memory structure.
10
+
11
+ **Usage:**
12
+ - `/import-docs docs/` - Analyze docs folder and create memories
13
+ - `/import-docs` - Interactive mode, asks for path
14
+
15
+ ## Overview
16
+
17
+ This command helps teams that have existing documentation migrate into ragtime's structured memory system. It:
18
+
19
+ 1. Scans all markdown files using `ragtime audit --json`
20
+ 2. Analyzes each document's content to classify properly
21
+ 3. Determines what should become memories vs. stay as indexed docs
22
+ 4. Creates memories in `.ragtime/app/` or `.ragtime/team/` as appropriate
23
+
24
+ ## Step 1: Get the Docs Path
25
+
26
+ **If `$ARGUMENTS` provided:**
27
+ - Use it as the docs path
28
+
29
+ **If no arguments:**
30
+ - Ask: "What docs folder should I analyze? (e.g., docs/, documentation/)"
31
+
32
+ ## Step 2: Run Audit
33
+
34
+ Run the ragtime audit command to get a structured view of all docs:
35
+
36
+ ```bash
37
+ ragtime audit {docs_path} --json
38
+ ```
39
+
40
+ Parse the JSON output to get the list of files and initial suggestions.
41
+
42
+ ## Step 3: Analyze Documents
43
+
44
+ For each document (or batch), read the content and classify:
45
+
46
+ ### Classification Questions
47
+
48
+ For each doc, determine:
49
+
50
+ 1. **Is this memory-worthy or just reference?**
51
+ - Memory-worthy: Contains decisions, patterns, architecture insights, conventions
52
+ - Reference: API docs, changelogs, auto-generated content, raw specs
53
+
54
+ 2. **What type of knowledge is it?**
55
+ - `architecture` - How systems/components work
56
+ - `decision` - Why we chose X over Y (look for "we decided", "because", trade-off discussion)
57
+ - `convention` - Team rules, coding standards, process docs
58
+ - `pattern` - Reusable solutions, "how to do X"
59
+ - `integration` - How external services connect
60
+ - `feature` - Feature documentation
61
+
62
+ 3. **What namespace?**
63
+ - `app` - Technical knowledge about this codebase
64
+ - `team` - Team conventions, process, standards
65
+
66
+ 4. **What component?** (infer from path and content)
67
+
68
+ 5. **Should this doc be split?**
69
+ - Large docs with multiple distinct sections may become multiple memories
70
+ - Each memory should be focused on ONE concept
71
+
72
+ ### Classification Hints
73
+
74
+ Look for signals in the content:
75
+
76
+ | Signal | Likely Type |
77
+ |--------|-------------|
78
+ | "We decided to..." | decision |
79
+ | "Always do X when Y" | convention |
80
+ | "The {system} works by..." | architecture |
81
+ | "To implement X, follow these steps..." | pattern |
82
+ | "Integrates with {service} via..." | integration |
83
+ | ADR format, numbered decisions | decision |
84
+ | API endpoints, request/response | architecture |
85
+ | Setup instructions, onboarding | convention |
86
+
87
+ ## Step 4: Choose Migration Strategy
88
+
89
+ Ask the user:
90
+
91
+ ```
92
+ How should I handle the original docs?
93
+
94
+ 1. **Memories only** - Create memories in .ragtime/, leave original docs unchanged
95
+ 2. **Frontmatter only** - Add frontmatter to original docs for better indexing, no memories
96
+ 3. **Both** - Add frontmatter to originals AND create memories for key insights (recommended)
97
+ ```
98
+
99
+ Based on their choice, adjust what the migration plan includes.
100
+
101
+ ## Step 5: Generate Migration Plan
102
+
103
+ Create a migration plan based on the user's strategy choice:
104
+
105
+ ```
106
+ ## Migration Plan
107
+
108
+ ### Will Add Frontmatter (if strategy includes frontmatter)
109
+
110
+ | File | Namespace | Type | Component |
111
+ |------|-----------|------|-----------|
112
+ | docs/auth/JWT_DESIGN.md | app | architecture | auth |
113
+ | docs/CODING_STANDARDS.md | team | convention | - |
114
+ | ... | | | |
115
+
116
+ ### Will Create Memories (if strategy includes memories)
117
+
118
+ | File | Type | Namespace | Component | Notes |
119
+ |------|------|-----------|-----------|-------|
120
+ | docs/auth/JWT_DESIGN.md | architecture | app | auth | Split into 2 memories |
121
+ | docs/CODING_STANDARDS.md | convention | team | - | Full doc |
122
+ | ... | | | | |
123
+
124
+ ### Will Index Only (no memory, just frontmatter or skip)
125
+
126
+ These stay as searchable docs but don't become memories:
127
+ - docs/CHANGELOG.md (reference)
128
+ - docs/api/endpoints.md (reference, auto-generated)
129
+ - ...
130
+
131
+ ### Will Skip (Z files)
132
+
133
+ - docs/archive/old-stuff.md (outdated)
134
+ - ...
135
+ ```
136
+
137
+ ## Step 6: Get Approval
138
+
139
+ Present the plan and ask:
140
+
141
+ ```
142
+ I've analyzed {total} docs:
143
+ - {X} will become memories in .ragtime/
144
+ - {Y} will be indexed as reference docs
145
+ - {Z} will be skipped
146
+
147
+ Review the plan above. Should I:
148
+ 1. Proceed with all
149
+ 2. Let me adjust some classifications
150
+ 3. Show me a specific file's analysis
151
+ 4. Cancel
152
+ ```
153
+
154
+ ## Step 7: Execute Migration
155
+
156
+ ### If adding frontmatter to originals:
157
+
158
+ For each doc that needs frontmatter, prepend:
159
+
160
+ ```yaml
161
+ ---
162
+ namespace: {app|team}
163
+ type: {type}
164
+ component: {if applicable}
165
+ ---
166
+ ```
167
+
168
+ This makes the original docs index better with `ragtime index`.
169
+
170
+ ### If creating memories:
171
+
172
+ 1. **Extract the key content** - Don't copy the whole doc verbatim unless it's focused. Extract the essential knowledge.
173
+
174
+ 2. **Write the memory file** to `.ragtime/app/{component}/{id}-{slug}.md` or `.ragtime/team/{id}-{slug}.md`:
175
+
176
+ ```yaml
177
+ ---
178
+ id: {8-char-uuid}
179
+ namespace: {app|team}
180
+ type: {type}
181
+ component: {if applicable}
182
+ confidence: medium
183
+ confidence_reason: import-docs
184
+ source: import-docs
185
+ status: active
186
+ added: {today}
187
+ migrated_from: {original-file-path}
188
+ ---
189
+
190
+ {extracted content}
191
+ ```
192
+
193
+ 3. **For split documents**, create multiple focused memories with related IDs.
194
+
195
+ ## Step 8: Reindex
196
+
197
+ After all memories are created:
198
+
199
+ ```bash
200
+ ragtime reindex
201
+ ```
202
+
203
+ ## Step 9: Summary
204
+
205
+ Summarize based on what was done:
206
+
207
+ ```
208
+ ## Migration Complete
209
+
210
+ ### Frontmatter Added (if applicable)
211
+ Updated {X} docs with frontmatter for better indexing.
212
+
213
+ ### Memories Created (if applicable)
214
+ Created {Y} memories:
215
+ - {N} architecture in app/
216
+ - {N} conventions in team/
217
+ - {N} decisions in app/
218
+ - ...
219
+
220
+ Next steps:
221
+ - Run 'ragtime index' to update the search index
222
+ - Review memories with: ragtime memories --namespace app
223
+ - Search with: ragtime search "your query"
224
+ - Edit any misclassified content in .ragtime/ or docs/
225
+ ```
226
+
227
+ ## Tips
228
+
229
+ - **Don't over-migrate**: Not every doc needs to be a memory. Reference docs are fine as indexed content.
230
+ - **Preserve originals**: This creates memories FROM docs, doesn't delete originals.
231
+ - **Review component inference**: The path-based component detection may need adjustment.
232
+ - **Batch by folder**: For large doc sets, consider migrating one folder at a time.
233
+
234
+ ## Example Session
235
+
236
+ ```
237
+ User: /import-docs docs/
238
+
239
+ Claude: I'll analyze your docs folder...
240
+
241
+ Running: ragtime audit docs/ --json
242
+
243
+ Found 45 markdown files. Let me analyze each one...
244
+
245
+ [Analyzes docs/auth/JWT_DESIGN.md]
246
+ This is an architecture doc about JWT implementation. Key insights:
247
+ - 15-minute access token expiry
248
+ - Refresh token rotation strategy
249
+ - Why we chose asymmetric keys
250
+
251
+ I'll create 1 memory in app/auth/ for this.
252
+
253
+ [Continues for other files...]
254
+
255
+ ## Migration Plan
256
+ ...
257
+
258
+ Proceed? (yes/adjust/cancel)
259
+ ```
@@ -1,9 +1,14 @@
1
1
  ---
2
- description: Graduate branch knowledge to app after PR merge
2
+ description: Graduate branch knowledge to app after PR merge (fallback)
3
3
  allowed-tools: Bash, mcp__ragtime__search, mcp__ragtime__graduate, mcp__ragtime__update_status, mcp__ragtime__remember, AskUserQuestion
4
4
  ---
5
5
 
6
- # PR Graduate: Curate Branch Knowledge
6
+ # PR Graduate: Curate Branch Knowledge (Post-Merge)
7
+
8
+ > **Preferred workflow:** Use `/create-pr` instead - it graduates memories *before*
9
+ > creating the PR so knowledge is committed alongside code.
10
+ >
11
+ > Use this command only if you already merged without graduating.
7
12
 
8
13
  After a PR is merged, review branch memories and decide what becomes permanent app knowledge.
9
14
 
@@ -36,11 +36,19 @@ class CodeConfig:
36
36
  ])
37
37
 
38
38
 
39
+ @dataclass
40
+ class ConventionsConfig:
41
+ """Configuration for convention checking."""
42
+ files: list[str] = field(default_factory=lambda: [".ragtime/CONVENTIONS.md"])
43
+ also_search_memories: bool = True
44
+
45
+
39
46
  @dataclass
40
47
  class RagtimeConfig:
41
48
  """Main ragtime configuration."""
42
49
  docs: DocsConfig = field(default_factory=DocsConfig)
43
50
  code: CodeConfig = field(default_factory=CodeConfig)
51
+ conventions: ConventionsConfig = field(default_factory=ConventionsConfig)
44
52
 
45
53
  @classmethod
46
54
  def load(cls, project_path: Path) -> "RagtimeConfig":
@@ -58,6 +66,7 @@ class RagtimeConfig:
58
66
 
59
67
  docs_data = data.get("docs", {})
60
68
  code_data = data.get("code", {})
69
+ conventions_data = data.get("conventions", {})
61
70
 
62
71
  return cls(
63
72
  docs=DocsConfig(
@@ -70,6 +79,12 @@ class RagtimeConfig:
70
79
  languages=code_data.get("languages", CodeConfig().languages),
71
80
  exclude=code_data.get("exclude", CodeConfig().exclude),
72
81
  ),
82
+ conventions=ConventionsConfig(
83
+ files=conventions_data.get("files", ConventionsConfig().files),
84
+ also_search_memories=conventions_data.get(
85
+ "also_search_memories", ConventionsConfig().also_search_memories
86
+ ),
87
+ ),
73
88
  )
74
89
 
75
90
  def save(self, project_path: Path) -> None:
@@ -89,6 +104,10 @@ class RagtimeConfig:
89
104
  "languages": self.code.languages,
90
105
  "exclude": self.code.exclude,
91
106
  },
107
+ "conventions": {
108
+ "files": self.conventions.files,
109
+ "also_search_memories": self.conventions.also_search_memories,
110
+ },
92
111
  }
93
112
 
94
113
  with open(config_path, "w") as f:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes