mindsystem-cc 3.0.0

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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +501 -0
  3. package/agents/ms-codebase-mapper.md +739 -0
  4. package/agents/ms-debugger.md +1184 -0
  5. package/agents/ms-designer.md +414 -0
  6. package/agents/ms-executor.md +760 -0
  7. package/agents/ms-integration-checker.md +423 -0
  8. package/agents/ms-milestone-auditor.md +448 -0
  9. package/agents/ms-mock-generator.md +182 -0
  10. package/agents/ms-plan-checker.md +746 -0
  11. package/agents/ms-research-synthesizer.md +248 -0
  12. package/agents/ms-researcher.md +962 -0
  13. package/agents/ms-roadmapper.md +606 -0
  14. package/agents/ms-verifier.md +779 -0
  15. package/agents/ms-verify-fixer.md +124 -0
  16. package/bin/install.js +296 -0
  17. package/commands/ms/add-phase.md +207 -0
  18. package/commands/ms/add-todo.md +182 -0
  19. package/commands/ms/audit-milestone.md +318 -0
  20. package/commands/ms/check-phase.md +162 -0
  21. package/commands/ms/check-todos.md +217 -0
  22. package/commands/ms/complete-milestone.md +137 -0
  23. package/commands/ms/create-roadmap.md +273 -0
  24. package/commands/ms/debug.md +149 -0
  25. package/commands/ms/define-requirements.md +121 -0
  26. package/commands/ms/design-phase.md +341 -0
  27. package/commands/ms/discuss-milestone.md +48 -0
  28. package/commands/ms/discuss-phase.md +60 -0
  29. package/commands/ms/do-work.md +90 -0
  30. package/commands/ms/execute-phase.md +289 -0
  31. package/commands/ms/help.md +623 -0
  32. package/commands/ms/insert-phase.md +227 -0
  33. package/commands/ms/list-phase-assumptions.md +50 -0
  34. package/commands/ms/map-codebase.md +71 -0
  35. package/commands/ms/new-milestone.md +193 -0
  36. package/commands/ms/new-project.md +338 -0
  37. package/commands/ms/pause-work.md +123 -0
  38. package/commands/ms/plan-milestone-gaps.md +285 -0
  39. package/commands/ms/plan-phase.md +105 -0
  40. package/commands/ms/progress.md +370 -0
  41. package/commands/ms/remove-phase.md +338 -0
  42. package/commands/ms/research-phase.md +175 -0
  43. package/commands/ms/research-project.md +339 -0
  44. package/commands/ms/resume-work.md +40 -0
  45. package/commands/ms/review-design.md +484 -0
  46. package/commands/ms/simplify-flutter.md +193 -0
  47. package/commands/ms/update.md +159 -0
  48. package/commands/ms/verify-work.md +92 -0
  49. package/commands/ms/whats-new.md +124 -0
  50. package/mindsystem/references/checkpoints.md +788 -0
  51. package/mindsystem/references/continuation-format.md +255 -0
  52. package/mindsystem/references/debugging/debugging-mindset.md +11 -0
  53. package/mindsystem/references/debugging/hypothesis-testing.md +11 -0
  54. package/mindsystem/references/debugging/investigation-techniques.md +11 -0
  55. package/mindsystem/references/debugging/verification-patterns.md +11 -0
  56. package/mindsystem/references/debugging/when-to-research.md +11 -0
  57. package/mindsystem/references/git-integration.md +254 -0
  58. package/mindsystem/references/goal-backward.md +286 -0
  59. package/mindsystem/references/mock-patterns.md +294 -0
  60. package/mindsystem/references/plan-format.md +473 -0
  61. package/mindsystem/references/principles.md +73 -0
  62. package/mindsystem/references/questioning.md +140 -0
  63. package/mindsystem/references/research-pitfalls.md +233 -0
  64. package/mindsystem/references/scope-estimation.md +256 -0
  65. package/mindsystem/references/tdd.md +263 -0
  66. package/mindsystem/references/verification-patterns.md +595 -0
  67. package/mindsystem/templates/DEBUG.md +159 -0
  68. package/mindsystem/templates/UAT.md +403 -0
  69. package/mindsystem/templates/adhoc-summary.md +153 -0
  70. package/mindsystem/templates/codebase/architecture.md +255 -0
  71. package/mindsystem/templates/codebase/concerns.md +310 -0
  72. package/mindsystem/templates/codebase/conventions.md +307 -0
  73. package/mindsystem/templates/codebase/integrations.md +280 -0
  74. package/mindsystem/templates/codebase/stack.md +186 -0
  75. package/mindsystem/templates/codebase/structure.md +285 -0
  76. package/mindsystem/templates/codebase/testing.md +480 -0
  77. package/mindsystem/templates/config.json +26 -0
  78. package/mindsystem/templates/context.md +140 -0
  79. package/mindsystem/templates/continue-here.md +78 -0
  80. package/mindsystem/templates/debug-subagent-prompt.md +91 -0
  81. package/mindsystem/templates/design-iteration.md +208 -0
  82. package/mindsystem/templates/design.md +417 -0
  83. package/mindsystem/templates/discovery.md +146 -0
  84. package/mindsystem/templates/milestone-archive.md +123 -0
  85. package/mindsystem/templates/milestone-context.md +93 -0
  86. package/mindsystem/templates/milestone.md +115 -0
  87. package/mindsystem/templates/phase-prompt.md +574 -0
  88. package/mindsystem/templates/project.md +184 -0
  89. package/mindsystem/templates/requirements.md +231 -0
  90. package/mindsystem/templates/research-project/ARCHITECTURE.md +204 -0
  91. package/mindsystem/templates/research-project/FEATURES.md +147 -0
  92. package/mindsystem/templates/research-project/PITFALLS.md +200 -0
  93. package/mindsystem/templates/research-project/STACK.md +120 -0
  94. package/mindsystem/templates/research-project/SUMMARY.md +170 -0
  95. package/mindsystem/templates/research-subagent-prompt.md +92 -0
  96. package/mindsystem/templates/research.md +529 -0
  97. package/mindsystem/templates/roadmap.md +214 -0
  98. package/mindsystem/templates/state.md +224 -0
  99. package/mindsystem/templates/summary.md +269 -0
  100. package/mindsystem/templates/user-setup.md +323 -0
  101. package/mindsystem/templates/verification-report.md +322 -0
  102. package/mindsystem/workflows/complete-milestone.md +759 -0
  103. package/mindsystem/workflows/create-milestone.md +203 -0
  104. package/mindsystem/workflows/debug.md +14 -0
  105. package/mindsystem/workflows/define-requirements.md +330 -0
  106. package/mindsystem/workflows/diagnose-issues.md +241 -0
  107. package/mindsystem/workflows/discovery-phase.md +293 -0
  108. package/mindsystem/workflows/discuss-milestone.md +310 -0
  109. package/mindsystem/workflows/discuss-phase.md +237 -0
  110. package/mindsystem/workflows/do-work.md +359 -0
  111. package/mindsystem/workflows/execute-phase.md +644 -0
  112. package/mindsystem/workflows/execute-plan.md +1828 -0
  113. package/mindsystem/workflows/generate-mocks.md +187 -0
  114. package/mindsystem/workflows/list-phase-assumptions.md +178 -0
  115. package/mindsystem/workflows/map-codebase.md +289 -0
  116. package/mindsystem/workflows/plan-phase.md +876 -0
  117. package/mindsystem/workflows/research-phase.md +17 -0
  118. package/mindsystem/workflows/research-project.md +23 -0
  119. package/mindsystem/workflows/resume-project.md +311 -0
  120. package/mindsystem/workflows/transition.md +564 -0
  121. package/mindsystem/workflows/verify-phase.md +629 -0
  122. package/mindsystem/workflows/verify-work.md +823 -0
  123. package/package.json +32 -0
  124. package/scripts/generate-phase-patch.sh +169 -0
  125. package/scripts/ms-lookup/README.md +112 -0
  126. package/scripts/ms-lookup/ms_lookup/__init__.py +3 -0
  127. package/scripts/ms-lookup/ms_lookup/__main__.py +6 -0
  128. package/scripts/ms-lookup/ms_lookup/backends/__init__.py +6 -0
  129. package/scripts/ms-lookup/ms_lookup/backends/context7.py +219 -0
  130. package/scripts/ms-lookup/ms_lookup/backends/perplexity.py +145 -0
  131. package/scripts/ms-lookup/ms_lookup/cache.py +48 -0
  132. package/scripts/ms-lookup/ms_lookup/cli.py +219 -0
  133. package/scripts/ms-lookup/ms_lookup/config.py +23 -0
  134. package/scripts/ms-lookup/ms_lookup/errors.py +24 -0
  135. package/scripts/ms-lookup/ms_lookup/output.py +49 -0
  136. package/scripts/ms-lookup/ms_lookup/tokens.py +56 -0
  137. package/scripts/ms-lookup/pyproject.toml +17 -0
  138. package/scripts/ms-lookup/uv.lock +207 -0
  139. package/scripts/ms-lookup-wrapper.sh +21 -0
@@ -0,0 +1,48 @@
1
+ """Caching layer using diskcache."""
2
+
3
+ import hashlib
4
+ from typing import Any
5
+
6
+ import diskcache
7
+
8
+ from ms_lookup.config import CACHE_DIR, CACHE_TTL_DOCS, CACHE_TTL_DEEP
9
+
10
+
11
+ def _get_cache() -> diskcache.Cache:
12
+ """Get or create cache instance."""
13
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
14
+ return diskcache.Cache(str(CACHE_DIR))
15
+
16
+
17
+ def _make_key(command: str, *args: str, max_tokens: int | None = None) -> str:
18
+ """Create cache key from command, args, and optionally max_tokens."""
19
+ key_parts = [command] + list(args)
20
+ if max_tokens is not None:
21
+ key_parts.append(str(max_tokens))
22
+ key_string = ":".join(key_parts)
23
+ key_hash = hashlib.sha256(key_string.encode()).hexdigest()[:16]
24
+ return f"{command}:{key_hash}"
25
+
26
+
27
+ def get_cached(command: str, *args: str, max_tokens: int | None = None) -> Any | None:
28
+ """Get cached result if exists and not expired."""
29
+ cache = _get_cache()
30
+ key = _make_key(command, *args, max_tokens=max_tokens)
31
+ return cache.get(key)
32
+
33
+
34
+ def set_cached(command: str, *args: str, max_tokens: int | None = None, value: Any) -> None:
35
+ """Cache a result with appropriate TTL."""
36
+ cache = _get_cache()
37
+ key = _make_key(command, *args, max_tokens=max_tokens)
38
+
39
+ # Select TTL based on command
40
+ ttl = CACHE_TTL_DOCS if command == "docs" else CACHE_TTL_DEEP
41
+
42
+ cache.set(key, value, expire=ttl)
43
+
44
+
45
+ def clear_cache() -> None:
46
+ """Clear all cached entries."""
47
+ cache = _get_cache()
48
+ cache.clear()
@@ -0,0 +1,219 @@
1
+ """Main CLI entry point for ms-lookup."""
2
+
3
+ import sys
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from ms_lookup import __version__
9
+ from ms_lookup.backends.context7 import Context7Client
10
+ from ms_lookup.backends.perplexity import PerplexityClient
11
+ from ms_lookup.cache import get_cached, set_cached
12
+ from ms_lookup.config import DEFAULT_MAX_TOKENS
13
+ from ms_lookup.errors import MsLookupError
14
+ from ms_lookup.output import format_error, format_success, output_json
15
+ from ms_lookup.tokens import estimate_tokens, truncate_results
16
+
17
+ app = typer.Typer(
18
+ name="ms-lookup",
19
+ help="Mindsystem research CLI - Context7 docs and Perplexity deep research",
20
+ add_completion=False,
21
+ )
22
+
23
+
24
+ def version_callback(value: bool) -> None:
25
+ """Print version and exit."""
26
+ if value:
27
+ typer.echo(f"ms-lookup {__version__}")
28
+ raise typer.Exit()
29
+
30
+
31
+ @app.callback()
32
+ def main(
33
+ version: Optional[bool] = typer.Option(
34
+ None,
35
+ "--version",
36
+ "-v",
37
+ callback=version_callback,
38
+ is_eager=True,
39
+ help="Show version and exit",
40
+ ),
41
+ ) -> None:
42
+ """Mindsystem research CLI - Context7 docs and Perplexity deep research."""
43
+ pass
44
+
45
+
46
+ @app.command()
47
+ def docs(
48
+ library: str = typer.Argument(..., help="Library name (e.g., 'nextjs', 'react')"),
49
+ query: str = typer.Argument(..., help="Documentation query"),
50
+ max_tokens: int = typer.Option(
51
+ DEFAULT_MAX_TOKENS,
52
+ "--max-tokens",
53
+ "-t",
54
+ help="Maximum tokens in response",
55
+ ),
56
+ no_cache: bool = typer.Option(
57
+ False,
58
+ "--no-cache",
59
+ help="Skip cache lookup",
60
+ ),
61
+ json_pretty: bool = typer.Option(
62
+ False,
63
+ "--json-pretty",
64
+ "-p",
65
+ help="Pretty-print JSON output",
66
+ ),
67
+ ) -> None:
68
+ """Query library documentation via Context7.
69
+
70
+ Use for authoritative, version-aware API documentation.
71
+
72
+ Examples:
73
+ ms-lookup docs nextjs "app router setup"
74
+ ms-lookup docs react "hooks useEffect cleanup"
75
+ ms-lookup docs "react-three-fiber" "physics integration"
76
+ """
77
+ command = "docs"
78
+
79
+ # Check cache first
80
+ if not no_cache:
81
+ cached = get_cached(command, library, query, max_tokens=max_tokens)
82
+ if cached is not None:
83
+ cached["metadata"]["cache_hit"] = True
84
+ typer.echo(output_json(cached, pretty=json_pretty))
85
+ return
86
+
87
+ try:
88
+ client = Context7Client()
89
+
90
+ # Resolve library name to ID
91
+ library_info = client.resolve_library(library, query)
92
+ library_id = library_info["library_id"]
93
+
94
+ # Query documentation
95
+ raw_response = client.query_docs(library_id, query)
96
+
97
+ # Format results
98
+ results = client.format_results(raw_response)
99
+ total_available = len(results)
100
+
101
+ # Truncate to token budget
102
+ truncated_results, tokens_used = truncate_results(results, max_tokens)
103
+
104
+ # Build response
105
+ metadata = {
106
+ "library_id": library_id,
107
+ "total_available": total_available,
108
+ "returned": len(truncated_results),
109
+ "tokens_used": tokens_used,
110
+ "max_tokens": max_tokens,
111
+ "cache_hit": False,
112
+ "confidence": "HIGH",
113
+ "backend": "context7",
114
+ }
115
+
116
+ response = format_success(
117
+ command=command,
118
+ query=query,
119
+ results=truncated_results,
120
+ metadata=metadata,
121
+ library=library,
122
+ )
123
+
124
+ # Cache result
125
+ if not no_cache:
126
+ set_cached(command, library, query, max_tokens=max_tokens, value=response)
127
+
128
+ typer.echo(output_json(response, pretty=json_pretty))
129
+
130
+ except MsLookupError as e:
131
+ error_response = format_error(command, e)
132
+ typer.echo(output_json(error_response, pretty=json_pretty))
133
+ raise typer.Exit(code=1)
134
+
135
+
136
+ @app.command()
137
+ def deep(
138
+ query: str = typer.Argument(..., help="Research query"),
139
+ no_cache: bool = typer.Option(
140
+ False,
141
+ "--no-cache",
142
+ help="Skip cache lookup",
143
+ ),
144
+ json_pretty: bool = typer.Option(
145
+ False,
146
+ "--json-pretty",
147
+ "-p",
148
+ help="Pretty-print JSON output",
149
+ ),
150
+ ) -> None:
151
+ """Perform research via Perplexity's reasoning model.
152
+
153
+ Uses chain-of-thought reasoning with web search to synthesize
154
+ actionable findings from multiple sources. Faster than exhaustive
155
+ deep research while maintaining quality.
156
+
157
+ Cost: ~$0.005 per query. Use for high-value technical questions.
158
+
159
+ Examples:
160
+ ms-lookup deep "authentication patterns for SaaS applications"
161
+ ms-lookup deep "WebGPU browser support and performance 2026"
162
+ ms-lookup deep "best practices for real-time collaboration"
163
+ """
164
+ command = "deep"
165
+
166
+ # Check cache first
167
+ if not no_cache:
168
+ cached = get_cached(command, query)
169
+ if cached is not None:
170
+ cached["metadata"]["cache_hit"] = True
171
+ typer.echo(output_json(cached, pretty=json_pretty))
172
+ return
173
+
174
+ try:
175
+ client = PerplexityClient()
176
+
177
+ # Perform research query
178
+ raw_response = client.query(query)
179
+
180
+ # Format results - no truncation for deep, output controlled via prompt
181
+ results, citations = client.format_results(raw_response)
182
+
183
+ # Calculate tokens for metadata (no truncation)
184
+ tokens_used = sum(estimate_tokens(r.get("content", "")) for r in results)
185
+
186
+ # Build response
187
+ metadata = {
188
+ "total_available": len(results),
189
+ "returned": len(results),
190
+ "tokens_used": tokens_used,
191
+ "cache_hit": False,
192
+ "confidence": "MEDIUM-HIGH",
193
+ "backend": "perplexity-reasoning",
194
+ }
195
+
196
+ if citations:
197
+ metadata["citations"] = citations
198
+
199
+ response = format_success(
200
+ command=command,
201
+ query=query,
202
+ results=results,
203
+ metadata=metadata,
204
+ )
205
+
206
+ # Cache result
207
+ if not no_cache:
208
+ set_cached(command, query, value=response)
209
+
210
+ typer.echo(output_json(response, pretty=json_pretty))
211
+
212
+ except MsLookupError as e:
213
+ error_response = format_error(command, e)
214
+ typer.echo(output_json(error_response, pretty=json_pretty))
215
+ raise typer.Exit(code=1)
216
+
217
+
218
+ if __name__ == "__main__":
219
+ app()
@@ -0,0 +1,23 @@
1
+ """Configuration and environment variables."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ # API Keys from environment
7
+ CONTEXT7_API_KEY = os.environ.get("CONTEXT7_API_KEY", "")
8
+ PERPLEXITY_API_KEY = os.environ.get("PERPLEXITY_API_KEY", "")
9
+
10
+ # API URLs
11
+ CONTEXT7_BASE_URL = "https://context7.com/api/v2"
12
+ PERPLEXITY_BASE_URL = "https://api.perplexity.ai"
13
+
14
+ # Cache configuration
15
+ CACHE_DIR = Path.home() / ".cache" / "ms-lookup"
16
+ CACHE_TTL_DOCS = 60 * 60 * 24 # 24 hours for docs
17
+ CACHE_TTL_DEEP = 60 * 60 * 6 # 6 hours for deep research
18
+
19
+ # Default token limits
20
+ DEFAULT_MAX_TOKENS = 2000
21
+
22
+ # Perplexity model - sonar-reasoning-pro for faster chain-of-thought research
23
+ PERPLEXITY_DEEP_MODEL = "sonar-reasoning-pro"
@@ -0,0 +1,24 @@
1
+ """Error codes and error handling."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class ErrorCode(str, Enum):
7
+ """Error codes for CLI responses."""
8
+
9
+ MISSING_API_KEY = "MISSING_API_KEY"
10
+ LIBRARY_NOT_FOUND = "LIBRARY_NOT_FOUND"
11
+ API_ERROR = "API_ERROR"
12
+ RATE_LIMITED = "RATE_LIMITED"
13
+ NETWORK_ERROR = "NETWORK_ERROR"
14
+ INVALID_RESPONSE = "INVALID_RESPONSE"
15
+
16
+
17
+ class MsLookupError(Exception):
18
+ """Base exception for ms-lookup errors."""
19
+
20
+ def __init__(self, code: ErrorCode, message: str, suggestions: list[str] | None = None):
21
+ self.code = code
22
+ self.message = message
23
+ self.suggestions = suggestions or []
24
+ super().__init__(message)
@@ -0,0 +1,49 @@
1
+ """JSON output formatting."""
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ from ms_lookup.errors import MsLookupError
7
+
8
+
9
+ def format_success(
10
+ command: str,
11
+ query: str,
12
+ results: list[dict],
13
+ metadata: dict,
14
+ library: str | None = None,
15
+ ) -> dict:
16
+ """Format successful response."""
17
+ response = {
18
+ "success": True,
19
+ "command": command,
20
+ "query": query,
21
+ "results": results,
22
+ "metadata": metadata,
23
+ }
24
+ if library:
25
+ response["library"] = library
26
+ return response
27
+
28
+
29
+ def format_error(command: str, error: MsLookupError) -> dict:
30
+ """Format error response."""
31
+ error_dict: dict[str, Any] = {
32
+ "code": error.code.value,
33
+ "message": error.message,
34
+ }
35
+ if error.suggestions:
36
+ error_dict["suggestions"] = error.suggestions
37
+
38
+ return {
39
+ "success": False,
40
+ "command": command,
41
+ "error": error_dict,
42
+ }
43
+
44
+
45
+ def output_json(data: dict, pretty: bool = False) -> str:
46
+ """Convert data to JSON string."""
47
+ if pretty:
48
+ return json.dumps(data, indent=2, ensure_ascii=False)
49
+ return json.dumps(data, ensure_ascii=False)
@@ -0,0 +1,56 @@
1
+ """Token estimation and truncation utilities."""
2
+
3
+
4
+ def estimate_tokens(text: str) -> int:
5
+ """Estimate token count using word count * 1.3."""
6
+ if not text:
7
+ return 0
8
+ word_count = len(text.split())
9
+ return int(word_count * 1.3)
10
+
11
+
12
+ def truncate_results(results: list[dict], max_tokens: int) -> tuple[list[dict], int]:
13
+ """Truncate results to fit within token budget.
14
+
15
+ Args:
16
+ results: List of result dicts with 'content' field
17
+ max_tokens: Maximum token budget
18
+
19
+ Returns:
20
+ Tuple of (truncated_results, total_tokens_used)
21
+ """
22
+ truncated = []
23
+ tokens_used = 0
24
+
25
+ for result in results:
26
+ content = result.get("content", "")
27
+ result_tokens = estimate_tokens(content)
28
+
29
+ # Add metadata overhead estimate (title, url, etc.)
30
+ overhead = 50
31
+ total_result_tokens = result_tokens + overhead
32
+
33
+ if tokens_used + total_result_tokens <= max_tokens:
34
+ result_with_tokens = {**result, "tokens": result_tokens}
35
+ truncated.append(result_with_tokens)
36
+ tokens_used += total_result_tokens
37
+ else:
38
+ # Try to fit partial content if we have room
39
+ remaining = max_tokens - tokens_used - overhead
40
+ if remaining > 100: # Only include if meaningful content fits
41
+ words = content.split()
42
+ truncated_word_count = int(remaining / 1.3)
43
+ if truncated_word_count > 0:
44
+ truncated_content = " ".join(words[:truncated_word_count]) + "..."
45
+ truncated_tokens = estimate_tokens(truncated_content)
46
+ result_with_tokens = {
47
+ **result,
48
+ "content": truncated_content,
49
+ "tokens": truncated_tokens,
50
+ "truncated": True
51
+ }
52
+ truncated.append(result_with_tokens)
53
+ tokens_used += truncated_tokens + overhead
54
+ break
55
+
56
+ return truncated, tokens_used
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "ms-lookup"
3
+ version = "1.0.0"
4
+ description = "CLI tool for Mindsystem research - Context7 docs and Perplexity deep research"
5
+ requires-python = ">=3.10"
6
+ dependencies = [
7
+ "typer>=0.9.0",
8
+ "httpx>=0.25.0",
9
+ "diskcache>=5.6.0",
10
+ ]
11
+
12
+ [project.scripts]
13
+ ms-lookup = "ms_lookup.cli:app"
14
+
15
+ [build-system]
16
+ requires = ["hatchling"]
17
+ build-backend = "hatchling.build"
@@ -0,0 +1,207 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.10"
4
+
5
+ [[package]]
6
+ name = "anyio"
7
+ version = "4.12.1"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ dependencies = [
10
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
11
+ { name = "idna" },
12
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
13
+ ]
14
+ sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" }
15
+ wheels = [
16
+ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
17
+ ]
18
+
19
+ [[package]]
20
+ name = "certifi"
21
+ version = "2026.1.4"
22
+ source = { registry = "https://pypi.org/simple" }
23
+ sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
24
+ wheels = [
25
+ { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
26
+ ]
27
+
28
+ [[package]]
29
+ name = "click"
30
+ version = "8.3.1"
31
+ source = { registry = "https://pypi.org/simple" }
32
+ dependencies = [
33
+ { name = "colorama", marker = "sys_platform == 'win32'" },
34
+ ]
35
+ sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
36
+ wheels = [
37
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
38
+ ]
39
+
40
+ [[package]]
41
+ name = "colorama"
42
+ version = "0.4.6"
43
+ source = { registry = "https://pypi.org/simple" }
44
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
45
+ wheels = [
46
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
47
+ ]
48
+
49
+ [[package]]
50
+ name = "diskcache"
51
+ version = "5.6.3"
52
+ source = { registry = "https://pypi.org/simple" }
53
+ sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" }
54
+ wheels = [
55
+ { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" },
56
+ ]
57
+
58
+ [[package]]
59
+ name = "exceptiongroup"
60
+ version = "1.3.1"
61
+ source = { registry = "https://pypi.org/simple" }
62
+ dependencies = [
63
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
64
+ ]
65
+ sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
66
+ wheels = [
67
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
68
+ ]
69
+
70
+ [[package]]
71
+ name = "gsd-lookup"
72
+ version = "1.0.0"
73
+ source = { editable = "." }
74
+ dependencies = [
75
+ { name = "diskcache" },
76
+ { name = "httpx" },
77
+ { name = "typer" },
78
+ ]
79
+
80
+ [package.metadata]
81
+ requires-dist = [
82
+ { name = "diskcache", specifier = ">=5.6.0" },
83
+ { name = "httpx", specifier = ">=0.25.0" },
84
+ { name = "typer", specifier = ">=0.9.0" },
85
+ ]
86
+
87
+ [[package]]
88
+ name = "h11"
89
+ version = "0.16.0"
90
+ source = { registry = "https://pypi.org/simple" }
91
+ sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
92
+ wheels = [
93
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
94
+ ]
95
+
96
+ [[package]]
97
+ name = "httpcore"
98
+ version = "1.0.9"
99
+ source = { registry = "https://pypi.org/simple" }
100
+ dependencies = [
101
+ { name = "certifi" },
102
+ { name = "h11" },
103
+ ]
104
+ sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
105
+ wheels = [
106
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
107
+ ]
108
+
109
+ [[package]]
110
+ name = "httpx"
111
+ version = "0.28.1"
112
+ source = { registry = "https://pypi.org/simple" }
113
+ dependencies = [
114
+ { name = "anyio" },
115
+ { name = "certifi" },
116
+ { name = "httpcore" },
117
+ { name = "idna" },
118
+ ]
119
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
120
+ wheels = [
121
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
122
+ ]
123
+
124
+ [[package]]
125
+ name = "idna"
126
+ version = "3.11"
127
+ source = { registry = "https://pypi.org/simple" }
128
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
129
+ wheels = [
130
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
131
+ ]
132
+
133
+ [[package]]
134
+ name = "markdown-it-py"
135
+ version = "4.0.0"
136
+ source = { registry = "https://pypi.org/simple" }
137
+ dependencies = [
138
+ { name = "mdurl" },
139
+ ]
140
+ sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
141
+ wheels = [
142
+ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
143
+ ]
144
+
145
+ [[package]]
146
+ name = "mdurl"
147
+ version = "0.1.2"
148
+ source = { registry = "https://pypi.org/simple" }
149
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
150
+ wheels = [
151
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
152
+ ]
153
+
154
+ [[package]]
155
+ name = "pygments"
156
+ version = "2.19.2"
157
+ source = { registry = "https://pypi.org/simple" }
158
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
159
+ wheels = [
160
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
161
+ ]
162
+
163
+ [[package]]
164
+ name = "rich"
165
+ version = "14.2.0"
166
+ source = { registry = "https://pypi.org/simple" }
167
+ dependencies = [
168
+ { name = "markdown-it-py" },
169
+ { name = "pygments" },
170
+ ]
171
+ sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
172
+ wheels = [
173
+ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
174
+ ]
175
+
176
+ [[package]]
177
+ name = "shellingham"
178
+ version = "1.5.4"
179
+ source = { registry = "https://pypi.org/simple" }
180
+ sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
181
+ wheels = [
182
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
183
+ ]
184
+
185
+ [[package]]
186
+ name = "typer"
187
+ version = "0.21.1"
188
+ source = { registry = "https://pypi.org/simple" }
189
+ dependencies = [
190
+ { name = "click" },
191
+ { name = "rich" },
192
+ { name = "shellingham" },
193
+ { name = "typing-extensions" },
194
+ ]
195
+ sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" }
196
+ wheels = [
197
+ { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" },
198
+ ]
199
+
200
+ [[package]]
201
+ name = "typing-extensions"
202
+ version = "4.15.0"
203
+ source = { registry = "https://pypi.org/simple" }
204
+ sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
205
+ wheels = [
206
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
207
+ ]
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+ # Wrapper script for ms-lookup CLI
3
+ # Handles uv/pip installation transparently
4
+
5
+ # Source uv environment if available
6
+ if [ -f "$HOME/.local/bin/env" ]; then
7
+ source "$HOME/.local/bin/env"
8
+ fi
9
+
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ cd "$SCRIPT_DIR/ms-lookup"
12
+
13
+ if command -v uv &> /dev/null; then
14
+ # Prefer uv if available (faster)
15
+ uv sync --quiet 2>/dev/null
16
+ uv run python -m ms_lookup "$@"
17
+ else
18
+ # Fall back to pip
19
+ pip install -q -e . 2>/dev/null
20
+ python3 -m ms_lookup "$@"
21
+ fi