intentic-ike 0.2.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.
Files changed (56) hide show
  1. intentic_ike-0.2.0/LICENSE +21 -0
  2. intentic_ike-0.2.0/PKG-INFO +27 -0
  3. intentic_ike-0.2.0/README.md +262 -0
  4. intentic_ike-0.2.0/ike/__init__.py +3 -0
  5. intentic_ike-0.2.0/ike/__main__.py +3 -0
  6. intentic_ike-0.2.0/ike/cli.py +56 -0
  7. intentic_ike-0.2.0/ike/commands/__init__.py +2 -0
  8. intentic_ike-0.2.0/ike/commands/doctor_cmd.py +33 -0
  9. intentic_ike-0.2.0/ike/commands/fetch_cmd.py +22 -0
  10. intentic_ike-0.2.0/ike/commands/flag_stale_cmd.py +19 -0
  11. intentic_ike-0.2.0/ike/commands/index_cmd.py +19 -0
  12. intentic_ike-0.2.0/ike/commands/init_cmd.py +37 -0
  13. intentic_ike-0.2.0/ike/commands/lint_cmd.py +19 -0
  14. intentic_ike-0.2.0/ike/commands/list_cmd.py +17 -0
  15. intentic_ike-0.2.0/ike/commands/migrate_mcp_cmd.py +30 -0
  16. intentic_ike-0.2.0/ike/commands/query_cmd.py +20 -0
  17. intentic_ike-0.2.0/ike/commands/route_cmd.py +19 -0
  18. intentic_ike-0.2.0/ike/commands/serve_cmd.py +17 -0
  19. intentic_ike-0.2.0/ike/commands/write_cmd.py +38 -0
  20. intentic_ike-0.2.0/ike/core/__init__.py +0 -0
  21. intentic_ike-0.2.0/ike/core/artifacts.py +146 -0
  22. intentic_ike-0.2.0/ike/core/doctor.py +167 -0
  23. intentic_ike-0.2.0/ike/core/engine.py +156 -0
  24. intentic_ike-0.2.0/ike/core/fetcher.py +54 -0
  25. intentic_ike-0.2.0/ike/core/index.py +245 -0
  26. intentic_ike-0.2.0/ike/core/init.py +52 -0
  27. intentic_ike-0.2.0/ike/core/linter.py +164 -0
  28. intentic_ike-0.2.0/ike/core/migrate.py +50 -0
  29. intentic_ike-0.2.0/ike/core/parser.py +317 -0
  30. intentic_ike-0.2.0/ike/core/serve.py +32 -0
  31. intentic_ike-0.2.0/ike/core/types.py +82 -0
  32. intentic_ike-0.2.0/ike/core/writer.py +210 -0
  33. intentic_ike-0.2.0/ike/mcp_server.py +93 -0
  34. intentic_ike-0.2.0/intentic_ike.egg-info/PKG-INFO +27 -0
  35. intentic_ike-0.2.0/intentic_ike.egg-info/SOURCES.txt +54 -0
  36. intentic_ike-0.2.0/intentic_ike.egg-info/dependency_links.txt +1 -0
  37. intentic_ike-0.2.0/intentic_ike.egg-info/entry_points.txt +2 -0
  38. intentic_ike-0.2.0/intentic_ike.egg-info/requires.txt +13 -0
  39. intentic_ike-0.2.0/intentic_ike.egg-info/top_level.txt +1 -0
  40. intentic_ike-0.2.0/pyproject.toml +49 -0
  41. intentic_ike-0.2.0/setup.cfg +4 -0
  42. intentic_ike-0.2.0/tests/test_artifacts.py +93 -0
  43. intentic_ike-0.2.0/tests/test_benchmark_old_vs_new.py +231 -0
  44. intentic_ike-0.2.0/tests/test_cli.py +106 -0
  45. intentic_ike-0.2.0/tests/test_doctor.py +134 -0
  46. intentic_ike-0.2.0/tests/test_engine.py +102 -0
  47. intentic_ike-0.2.0/tests/test_fetcher.py +59 -0
  48. intentic_ike-0.2.0/tests/test_index.py +163 -0
  49. intentic_ike-0.2.0/tests/test_init.py +61 -0
  50. intentic_ike-0.2.0/tests/test_integration_intentic_kb.py +257 -0
  51. intentic_ike-0.2.0/tests/test_linter.py +100 -0
  52. intentic_ike-0.2.0/tests/test_mcp.py +93 -0
  53. intentic_ike-0.2.0/tests/test_migrate.py +87 -0
  54. intentic_ike-0.2.0/tests/test_parser.py +248 -0
  55. intentic_ike-0.2.0/tests/test_serve.py +38 -0
  56. intentic_ike-0.2.0/tests/test_writer.py +121 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 intentic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.4
2
+ Name: intentic-ike
3
+ Version: 0.2.0
4
+ Summary: intentic Knowledge Engine — context-efficient knowledge serving for AI agents
5
+ Author-email: intentic <hello@intentic.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/pedrams/ike
8
+ Project-URL: Repository, https://github.com/pedrams/ike
9
+ Keywords: knowledge-engine,mcp,ai-agents,knowledge-base,rag
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.12
15
+ License-File: LICENSE
16
+ Requires-Dist: click>=8.1
17
+ Requires-Dist: python-frontmatter>=1.1
18
+ Requires-Dist: markdown-it-py>=3.0
19
+ Requires-Dist: tiktoken>=0.8
20
+ Requires-Dist: filelock>=3.16
21
+ Requires-Dist: mdformat>=0.7.17
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=6.0; extra == "dev"
25
+ Provides-Extra: mcp
26
+ Requires-Dist: fastmcp>=2.0; extra == "mcp"
27
+ Dynamic: license-file
@@ -0,0 +1,262 @@
1
+ # ike — intentic Knowledge Engine
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://python.org)
5
+ [![PyPI](https://img.shields.io/badge/pypi-intentic--ike-orange.svg)](https://pypi.org/project/intentic-ike/)
6
+
7
+ **Make your Markdown knowledge base queryable by any AI tool — in one command.**
8
+
9
+ ```bash
10
+ pip install intentic-ike[mcp]
11
+ ike init --kb-root ./docs
12
+ ```
13
+
14
+ ike scans your docs, fixes quality issues, and generates config files so that Claude Code, Cursor, Codex, Windsurf, and every other AI coding tool can query your knowledge base with precision.
15
+
16
+ **Before ike:** Your AI tool reads entire files, wastes context, gets confused.
17
+ **After ike:** Your AI tool asks for exactly the section it needs. 77-94% fewer tokens.
18
+
19
+ ```
20
+ # Without ike: 17,490 tokens (4 full files dumped into context)
21
+ $ cat company/vision.md company/icp.md company/positioning.md company/messaging.md | wc -w
22
+
23
+ # With ike: 3,969 tokens (8 targeted sections)
24
+ $ ike route "ICP definition"
25
+ → company/icp-definition.md#ideal-customer-profile-icp (2,836 tokens)
26
+ $ ike fetch company/icp-definition.md --section ideal-customer-profile-icp
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Option A: Let your AI tool do it
32
+
33
+ Open your AI editor and paste this prompt:
34
+
35
+ <details>
36
+ <summary><strong>Copy this prompt into Claude Code, Cursor, Codex, or any AI coding tool</strong></summary>
37
+
38
+ ```
39
+ I need to set up ike (intentic Knowledge Engine) to make this repository's
40
+ documentation queryable by AI tools.
41
+
42
+ ike is a CLI + MCP server that indexes Markdown files and serves them via
43
+ 2-step retrieval: route (find relevant sections, ~100 tokens) then fetch
44
+ (load specific content). This is much more efficient than reading entire
45
+ files — 77-94% token savings.
46
+
47
+ Repo: https://github.com/pedrams/ike
48
+ Docs: https://github.com/pedrams/ike/blob/main/docs/build-with-ai.md
49
+
50
+ Please do the following:
51
+
52
+ 1. INSTALL: Run `pip install intentic-ike[mcp]`
53
+ - Requires Python 3.12+
54
+ - The [mcp] extra includes the MCP server for IDE integration
55
+
56
+ 2. FIND MY DOCS: Look for the directory containing my Markdown documentation.
57
+ Common locations: ./docs, ./knowledge, ./wiki, or the repo root.
58
+ List the .md files you find so I can confirm.
59
+
60
+ 3. INITIALIZE: Run `ike init --kb-root <path-to-docs>`
61
+ This will:
62
+ - Scan all .md files and report quality (ready/fixable/needs review)
63
+ - Generate .mcp.json (MCP server config for Claude Code, Cursor, etc.)
64
+ - Generate AGENTS.md (universal AI tool discovery file)
65
+ If .mcp.json already exists, ike merges — it won't overwrite your config.
66
+
67
+ 4. FIX DOCS: Run `ike doctor --yes`
68
+ This auto-adds missing YAML frontmatter (title, domain, summary) to docs
69
+ that need it. Only metadata is added — document content is never changed.
70
+ If you prefer to review each fix: run `ike doctor` without --yes.
71
+
72
+ 5. VERIFY: Run these commands and show me the output:
73
+ - `ike route "test"` — should return matching sections with token counts
74
+ - `ike lint` — should show remaining quality issues (if any)
75
+ - `cat .mcp.json` — should show the MCP server config
76
+ - `cat AGENTS.md` — should show the AI tool discovery file
77
+
78
+ 6. DONE: Tell me to restart my AI tool so the MCP connection activates.
79
+
80
+ If any step fails:
81
+ - "ike: command not found" → pip install didn't work, try: python -m ike --help
82
+ - "ike serve fails with ImportError" → install with MCP: pip install intentic-ike[mcp]
83
+ - "route returns no results" → run: ike index --rebuild
84
+ - "doctor changes too much" → use: ike doctor (interactive) instead of --yes
85
+ ```
86
+
87
+ </details>
88
+
89
+ ### Option B: Manual setup (4 commands)
90
+
91
+ ```bash
92
+ pip install intentic-ike[mcp] # 1. Install
93
+ cd ~/my-repo
94
+ ike init --kb-root ./docs # 2. Scan docs, generate .mcp.json + AGENTS.md
95
+ ike doctor --yes # 3. Fix missing frontmatter
96
+ ike route "test" # 4. Verify
97
+ # Restart your AI tool
98
+ ```
99
+
100
+ ## How It Works
101
+
102
+ **2-step retrieval:** Route first (~100 tokens), then fetch only what you need.
103
+
104
+ ```bash
105
+ # Step 1: Find relevant sections
106
+ $ ike route "deployment strategy"
107
+ {
108
+ "chunks": [
109
+ {"file_path": "engineering/architecture.md",
110
+ "section_id": "deployment", "score": 3.0, "token_count": 842}
111
+ ]
112
+ }
113
+
114
+ # Step 2: Load only what you need
115
+ $ ike fetch engineering/architecture.md --section deployment
116
+ ## Deployment
117
+ CI/CD pipeline deploys to Hetzner Cloud...
118
+ ```
119
+
120
+ The AI tool decides what to load based on token counts. No context wasted.
121
+
122
+ ## AI Tool Compatibility
123
+
124
+ ike generates discovery files for every major AI coding tool:
125
+
126
+ | AI Tool | How it discovers ike | Generated by |
127
+ |---------|---------------------|-------------|
128
+ | **Claude Code** | `.mcp.json` + `AGENTS.md` | `ike init` |
129
+ | **Cursor** | `.mcp.json` + `AGENTS.md` | `ike init` |
130
+ | **Codex** (OpenAI) | `AGENTS.md` | `ike init` |
131
+ | **Windsurf** | `.mcp.json` + `AGENTS.md` | `ike init` |
132
+ | **Claude Desktop** | `.mcp.json` | `ike init` |
133
+ | **OpenCode** | `AGENTS.md` | `ike init` |
134
+ | **Hermes** | `.mcp.json` + `AGENTS.md` | `ike init` |
135
+ | **VS Code + Copilot** | `.mcp.json` | `ike init` |
136
+ | **Zed** | `.mcp.json` | `ike init` |
137
+ | **Aider** | `AGENTS.md` | `ike init` |
138
+
139
+ **MCP tools** (for MCP-capable editors): `route`, `fetch`, `query`, `lint`
140
+ **CLI commands** (for everything else): `ike route`, `ike fetch`, `ike query`, `ike lint`
141
+
142
+ ## Commands
143
+
144
+ ### Setup
145
+
146
+ | Command | What it does |
147
+ |---------|-------------|
148
+ | `ike init --kb-root ./docs` | Scan docs, generate `.mcp.json` + `AGENTS.md` |
149
+ | `ike doctor --yes` | Auto-fix missing frontmatter |
150
+ | `ike doctor` | Interactive frontmatter fix (review each) |
151
+ | `ike serve` | Start MCP server (stdio transport) |
152
+ | `ike migrate-mcp` | Migrate old `.mcp.json` to portable format |
153
+
154
+ ### Query
155
+
156
+ | Command | What it does |
157
+ |---------|-------------|
158
+ | `ike route "query"` | Find relevant sections (~100 tokens response) |
159
+ | `ike fetch path/file.md` | Load entire file |
160
+ | `ike fetch path/file.md --section id` | Load specific section |
161
+ | `ike query "text" --depth deep` | Route + fetch in one step |
162
+
163
+ ### Maintenance
164
+
165
+ | Command | What it does |
166
+ |---------|-------------|
167
+ | `ike lint` | Check for missing frontmatter, broken refs, orphans |
168
+ | `ike lint --freshness 30` | Also flag docs older than 30 days |
169
+ | `ike list` | List all indexed sections |
170
+ | `ike index --rebuild` | Rebuild search index |
171
+
172
+ ### Global options
173
+
174
+ ```bash
175
+ ike --kb-root /path/to/kb route "query" # custom KB root
176
+ export IKE_KB_ROOT=/path/to/kb # or via env var
177
+ ike --version # show version
178
+ ```
179
+
180
+ ## Context Efficiency
181
+
182
+ Tested against a real 81-doc knowledge base:
183
+
184
+ | Scenario | Without ike | With ike | Savings |
185
+ |----------|------------|---------|---------|
186
+ | Signal analysis (4 company files) | 17,490 tokens | 3,969 tokens | **-77%** |
187
+ | Ripple analysis (search KB) | 17,716 tokens | 2,811 tokens | **-84%** |
188
+ | Architecture lookup (1 section) | 4,299 tokens | 259 tokens | **-94%** |
189
+ | Vision section (from 7K doc) | 7,175 tokens | 807 tokens | **-89%** |
190
+
191
+ Every token saved is a token available for reasoning.
192
+
193
+ ## Document Format
194
+
195
+ ike works with any Markdown. For best results, docs should have YAML frontmatter:
196
+
197
+ ```yaml
198
+ ---
199
+ title: "Authentication Architecture"
200
+ domain: "engineering"
201
+ summary: "OAuth2 + JWT auth flow for the API gateway."
202
+ ---
203
+ ```
204
+
205
+ **Don't have frontmatter?** Run `ike doctor --yes` — it infers title from H1, domain from directory path, summary from the first paragraph.
206
+
207
+ ## Install
208
+
209
+ ```bash
210
+ pip install intentic-ike # CLI only
211
+ pip install intentic-ike[mcp] # CLI + MCP server
212
+ ```
213
+
214
+ Requires Python 3.12+. Works on Linux, macOS, Windows (WSL).
215
+
216
+ ## MCP Server Configuration
217
+
218
+ `ike init` generates `.mcp.json` automatically. Or configure manually:
219
+
220
+ ```json
221
+ {
222
+ "mcpServers": {
223
+ "ike": {
224
+ "command": "ike",
225
+ "args": ["serve"],
226
+ "env": { "IKE_KB_ROOT": "/path/to/your/docs" }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Architecture
233
+
234
+ ```
235
+ AI Tool (Claude Code, Cursor, Codex, ...)
236
+ |
237
+ ike CLI / MCP Server
238
+ |
239
+ Engine (facade)
240
+ |
241
+ ┌────┬────┬────┬────┐
242
+ Parser Index Fetcher Writer Linter
243
+ | |
244
+ markdown-it SQLite WAL
245
+ ```
246
+
247
+ - **Plugin CLI** — Commands auto-discovered from `ike/commands/*_cmd.py`
248
+ - **Thread-safe** — SQLite with per-thread connections (safe for MCP thread pool)
249
+ - **Lazy MCP** — `fastmcp` only imported when `ike serve` runs
250
+
251
+ ## Development
252
+
253
+ ```bash
254
+ git clone https://github.com/pedrams/ike && cd ike
255
+ python -m venv .venv && source .venv/bin/activate
256
+ pip install -e ".[dev,mcp]"
257
+ pytest # 184 tests, ~5s
258
+ ```
259
+
260
+ ## License
261
+
262
+ MIT
@@ -0,0 +1,3 @@
1
+ """intentic Knowledge Engine — context-efficient knowledge serving for AI agents."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from ike.cli import cli
2
+
3
+ cli()
@@ -0,0 +1,56 @@
1
+ """ike CLI — dynamic command loading from ike/commands/.
2
+
3
+ Each *_cmd.py in ike/commands/ exports a Click command as `cmd`.
4
+ Commands are auto-discovered at CLI startup.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib
10
+ from pathlib import Path
11
+
12
+ import click
13
+
14
+ from .core.engine import Engine
15
+
16
+
17
+ class DynamicGroup(click.Group):
18
+ """Click group that auto-discovers commands from ike/commands/*_cmd.py."""
19
+
20
+ def __init__(self, *args, **kwargs):
21
+ super().__init__(*args, **kwargs)
22
+ self._load_commands()
23
+
24
+ def _load_commands(self) -> None:
25
+ commands_dir = Path(__file__).parent / "commands"
26
+ if not commands_dir.exists():
27
+ return
28
+ for f in sorted(commands_dir.glob("*_cmd.py")):
29
+ name = f.stem.removesuffix("_cmd").replace("_", "-")
30
+ try:
31
+ mod = importlib.import_module(f"ike.commands.{f.stem}")
32
+ if hasattr(mod, "cmd"):
33
+ self.add_command(mod.cmd, name)
34
+ except Exception:
35
+ pass # Graceful: skip broken commands
36
+
37
+
38
+ @click.group(cls=DynamicGroup)
39
+ @click.version_option(package_name="intentic-ike")
40
+ @click.option(
41
+ "--kb-root",
42
+ type=click.Path(),
43
+ default=None,
44
+ envvar="IKE_KB_ROOT",
45
+ help="KB root directory (default: ~/intentic-kb)",
46
+ )
47
+ @click.pass_context
48
+ def cli(ctx: click.Context, kb_root: str | None) -> None:
49
+ """ike — intentic Knowledge Engine."""
50
+ ctx.ensure_object(dict)
51
+ kb = Path(kb_root).expanduser() if kb_root else None
52
+ ctx.obj["kb_root"] = kb
53
+ try:
54
+ ctx.obj["engine"] = Engine(kb_root=kb)
55
+ except Exception:
56
+ ctx.obj["engine"] = None
@@ -0,0 +1,2 @@
1
+ # ike/commands/ — Dynamic CLI command modules.
2
+ # Each *_cmd.py must export a Click command as `cmd`.
@@ -0,0 +1,33 @@
1
+ """ike doctor — auto-fix missing frontmatter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+
10
+ @click.command()
11
+ @click.option("--yes", "auto_fix", is_flag=True, help="Apply all fixes without prompting")
12
+ @click.option(
13
+ "--kb-root",
14
+ type=click.Path(exists=True),
15
+ default=None,
16
+ help="KB root (defaults to --kb-root from parent group or IKE_KB_ROOT)",
17
+ )
18
+ @click.pass_context
19
+ def cmd(ctx: click.Context, auto_fix: bool, kb_root: str | None) -> None:
20
+ """Auto-fix missing frontmatter in KB documents."""
21
+ from ike.core.doctor import run_doctor
22
+
23
+ if kb_root:
24
+ kb = Path(kb_root).resolve()
25
+ else:
26
+ kb = ctx.obj.get("kb_root")
27
+ if kb is None:
28
+ kb = Path.home() / "intentic-kb"
29
+
30
+ fixed = run_doctor(kb, auto_fix=auto_fix)
31
+ click.echo(f"\nFixed {fixed} file(s).")
32
+ if fixed > 0:
33
+ click.echo("Run `ike lint` to verify.")
@@ -0,0 +1,22 @@
1
+ """Load KB content (entire file or specific section)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command()
9
+ @click.argument("file_path")
10
+ @click.option("--section", default=None, help="Section ID to fetch")
11
+ @click.pass_context
12
+ def cmd(ctx: click.Context, file_path: str, section: str | None) -> None:
13
+ """Load KB content (entire file or specific section)."""
14
+ try:
15
+ result = ctx.obj["engine"].fetch(file_path, section)
16
+ click.echo(result.content)
17
+ except KeyError as e:
18
+ click.echo(f"Error: {e}", err=True)
19
+ raise SystemExit(1)
20
+ except FileNotFoundError:
21
+ click.echo(f"Error: File not found: {file_path}", err=True)
22
+ raise SystemExit(1)
@@ -0,0 +1,19 @@
1
+ """Flag a section as stale."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command("flag-stale")
9
+ @click.argument("file_path")
10
+ @click.option("--section", required=True, help="Section ID to flag")
11
+ @click.option("--reason", required=True, help="Why the section is stale")
12
+ @click.pass_context
13
+ def cmd(ctx: click.Context, file_path: str, section: str, reason: str) -> None:
14
+ """Flag a section as stale."""
15
+ result = ctx.obj["engine"].flag_stale(file_path, section, reason)
16
+ if not result.success:
17
+ click.echo(f"Error: {result.error}", err=True)
18
+ raise SystemExit(1)
19
+ click.echo(f"Flagged {file_path}#{section} as stale: {reason}")
@@ -0,0 +1,19 @@
1
+ """Build or rebuild the search index."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command()
9
+ @click.option("--rebuild", is_flag=True, help="Full rebuild (drop + reindex)")
10
+ @click.pass_context
11
+ def cmd(ctx: click.Context, rebuild: bool) -> None:
12
+ """Build or rebuild the search index."""
13
+ engine = ctx.obj["engine"]
14
+ if rebuild:
15
+ n = engine.rebuild_index()
16
+ click.echo(f"Index rebuilt: {n} chunks indexed.")
17
+ else:
18
+ engine.index.ensure_fresh(engine.parser)
19
+ click.echo("Index is fresh.")
@@ -0,0 +1,37 @@
1
+ """ike init — bootstrap ike for a repository."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+
10
+ @click.command()
11
+ @click.option(
12
+ "--kb-root",
13
+ type=click.Path(exists=True),
14
+ required=True,
15
+ help="Path to the knowledge base directory",
16
+ )
17
+ @click.pass_context
18
+ def cmd(ctx: click.Context, kb_root: str) -> None:
19
+ """Bootstrap ike for a repository. Scans docs and generates discovery artifacts."""
20
+ from ike.core.init import run_init
21
+
22
+ kb = Path(kb_root).resolve()
23
+ cwd = Path.cwd()
24
+
25
+ result = run_init(kb, cwd)
26
+
27
+ click.echo(f"Found {result['total']} markdown files in {kb}")
28
+ click.echo()
29
+ click.echo("Quality Report:")
30
+ click.echo(f" {result['ready']} ready")
31
+ click.echo(f" {result['fixable']} fixable (run `ike doctor` to fix)")
32
+ click.echo(f" {result['needs_review']} need review")
33
+ click.echo()
34
+ for a in result["artifacts"]:
35
+ click.echo(f"Created: {a}")
36
+ click.echo()
37
+ click.echo("Restart your AI tool to activate ike MCP.")
@@ -0,0 +1,19 @@
1
+ """Run consistency checks on the KB."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command()
9
+ @click.option("--freshness", type=int, default=None, help="Flag docs older than N days")
10
+ @click.pass_context
11
+ def cmd(ctx: click.Context, freshness: int | None) -> None:
12
+ """Run consistency checks on the KB."""
13
+ findings = ctx.obj["engine"].lint(freshness)
14
+ if not findings:
15
+ click.echo("No issues found.")
16
+ return
17
+ for f in findings:
18
+ click.echo(f"[{f.severity}] {f.file_path}: {f.check} — {f.message}")
19
+ click.echo(f"\n{len(findings)} issue(s) found.")
@@ -0,0 +1,17 @@
1
+ """List all KB documents and sections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command("list")
9
+ @click.option("--type", "knowledge_type", default=None, help="Filter by type")
10
+ @click.pass_context
11
+ def cmd(ctx: click.Context, knowledge_type: str | None) -> None:
12
+ """List all KB documents and sections."""
13
+ chunks = ctx.obj["engine"].list_docs(knowledge_type)
14
+ for c in chunks:
15
+ section = f"#{c.section_id}" if c.section_id else ""
16
+ click.echo(f"{c.file_path}{section} ({c.token_count} tokens)")
17
+ click.echo(f"\n{len(chunks)} section(s).")
@@ -0,0 +1,30 @@
1
+ """ike migrate-mcp — migrate .mcp.json to portable pattern."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import click
9
+
10
+
11
+ @click.command("migrate-mcp")
12
+ @click.option("--dry-run", is_flag=True, help="Show changes without applying")
13
+ def cmd(dry_run: bool) -> None:
14
+ """Migrate .mcp.json from absolute paths to portable uvx/pipx pattern."""
15
+ from ike.core.migrate import run_migrate_mcp
16
+
17
+ result = run_migrate_mcp(Path.cwd(), dry_run=dry_run)
18
+
19
+ if not result["changed"]:
20
+ click.echo(f"No migration needed: {result.get('reason', 'unknown')}")
21
+ return
22
+
23
+ if dry_run:
24
+ click.echo("DRY RUN — would change:")
25
+ click.echo(f" Old: {json.dumps(result['old'], indent=2)}")
26
+ click.echo(f" New: {json.dumps(result['new'], indent=2)}")
27
+ else:
28
+ click.echo("Migrated .mcp.json:")
29
+ click.echo(f" Old command: {result['old'].get('command')}")
30
+ click.echo(f" New command: {result['new'].get('command')}")
@@ -0,0 +1,20 @@
1
+ """Route + fetch in one step."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+
8
+ @click.command()
9
+ @click.argument("query_text")
10
+ @click.option("--depth", type=click.Choice(["shallow", "deep"]), default="shallow")
11
+ @click.option("--limit", default=5)
12
+ @click.pass_context
13
+ def cmd(ctx: click.Context, query_text: str, depth: str, limit: int) -> None:
14
+ """Route + fetch in one step."""
15
+ results = ctx.obj["engine"].query_and_fetch(query_text, depth, limit)
16
+ for r in results:
17
+ section_label = f"#{r.section_id}" if r.section_id else ""
18
+ click.echo(f"--- {r.file_path}{section_label} ({r.token_count} tokens) ---")
19
+ click.echo(r.content)
20
+ click.echo()
@@ -0,0 +1,19 @@
1
+ """Route a query to relevant KB sections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import asdict
7
+
8
+ import click
9
+
10
+
11
+ @click.command()
12
+ @click.argument("query_text")
13
+ @click.option("--limit", default=10, help="Max results")
14
+ @click.option("--type", "knowledge_type", default=None, help="Filter by type")
15
+ @click.pass_context
16
+ def cmd(ctx: click.Context, query_text: str, limit: int, knowledge_type: str | None) -> None:
17
+ """Find relevant KB sections. Returns paths + token counts."""
18
+ result = ctx.obj["engine"].route(query_text, limit, knowledge_type)
19
+ click.echo(json.dumps(asdict(result), indent=2))
@@ -0,0 +1,17 @@
1
+ """ike serve — start the MCP server.
2
+
3
+ CRITICAL: No top-level imports of fastmcp or ike.mcp_server.
4
+ CRITICAL: No print(), sys.stdout.write(), or click.echo() — stdout is for JSON-RPC.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import click
10
+
11
+
12
+ @click.command()
13
+ def cmd() -> None:
14
+ """Start the ike MCP server (stdio transport)."""
15
+ from ike.core.serve import run_serve
16
+
17
+ run_serve()