misterdev 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. misterdev/__init__.py +3 -0
  2. misterdev/agent.py +2166 -0
  3. misterdev/agent_helpers.py +194 -0
  4. misterdev/analyzers/__init__.py +0 -0
  5. misterdev/analyzers/project_analyzer/__init__.py +246 -0
  6. misterdev/analyzers/project_analyzer/detection.py +146 -0
  7. misterdev/analyzers/project_analyzer/merge.py +137 -0
  8. misterdev/analyzers/project_analyzer/overview.py +312 -0
  9. misterdev/analyzers/project_analyzer/prompts.py +83 -0
  10. misterdev/cli.py +370 -0
  11. misterdev/config.py +521 -0
  12. misterdev/core/__init__.py +0 -0
  13. misterdev/core/audit.py +88 -0
  14. misterdev/core/config.py +10 -0
  15. misterdev/core/context/__init__.py +0 -0
  16. misterdev/core/context/change_tracker.py +218 -0
  17. misterdev/core/context/contracts/__init__.py +63 -0
  18. misterdev/core/context/contracts/_log.py +9 -0
  19. misterdev/core/context/contracts/_text.py +12 -0
  20. misterdev/core/context/contracts/extraction.py +30 -0
  21. misterdev/core/context/contracts/python_generic.py +42 -0
  22. misterdev/core/context/contracts/registry.py +127 -0
  23. misterdev/core/context/contracts/rust_line.py +225 -0
  24. misterdev/core/context/contracts/rust_tree_sitter.py +141 -0
  25. misterdev/core/context/lsp.py +174 -0
  26. misterdev/core/context/scratchpad.py +111 -0
  27. misterdev/core/context/topography/__init__.py +43 -0
  28. misterdev/core/context/topography/_log.py +10 -0
  29. misterdev/core/context/topography/cache.py +91 -0
  30. misterdev/core/context/topography/engine.py +172 -0
  31. misterdev/core/context/topography/graph.py +675 -0
  32. misterdev/core/context/topography/nodes.py +55 -0
  33. misterdev/core/context/topography/parsers.py +95 -0
  34. misterdev/core/context/topography/syntax.py +54 -0
  35. misterdev/core/economics/__init__.py +0 -0
  36. misterdev/core/economics/context_budget.py +175 -0
  37. misterdev/core/economics/embeddings.py +232 -0
  38. misterdev/core/economics/free_models.py +108 -0
  39. misterdev/core/economics/llm_cache.py +105 -0
  40. misterdev/core/economics/model_catalog.py +79 -0
  41. misterdev/core/economics/model_ledger.py +331 -0
  42. misterdev/core/economics/model_selector.py +281 -0
  43. misterdev/core/execution/__init__.py +0 -0
  44. misterdev/core/execution/bounded.py +50 -0
  45. misterdev/core/execution/container.py +221 -0
  46. misterdev/core/execution/error_classifier.py +366 -0
  47. misterdev/core/execution/error_resolver.py +201 -0
  48. misterdev/core/execution/governance.py +283 -0
  49. misterdev/core/execution/outcomes.py +50 -0
  50. misterdev/core/execution/progress.py +120 -0
  51. misterdev/core/execution/project.py +231 -0
  52. misterdev/core/execution/registry.py +97 -0
  53. misterdev/core/execution/runtime.py +279 -0
  54. misterdev/core/gitcmd.py +39 -0
  55. misterdev/core/integration/__init__.py +0 -0
  56. misterdev/core/integration/mcp.py +368 -0
  57. misterdev/core/integration/mcp_gather.py +186 -0
  58. misterdev/core/models.py +35 -0
  59. misterdev/core/modes.py +184 -0
  60. misterdev/core/planning/__init__.py +0 -0
  61. misterdev/core/planning/advisor.py +89 -0
  62. misterdev/core/planning/assessment.py +135 -0
  63. misterdev/core/planning/decomposer.py +387 -0
  64. misterdev/core/planning/metacognition.py +103 -0
  65. misterdev/core/planning/sovereign.py +308 -0
  66. misterdev/core/planning/targets.py +201 -0
  67. misterdev/core/reporting/__init__.py +0 -0
  68. misterdev/core/reporting/report.py +377 -0
  69. misterdev/core/reporting/report_view.py +151 -0
  70. misterdev/core/task.py +163 -0
  71. misterdev/core/verification/__init__.py +0 -0
  72. misterdev/core/verification/claim_verifier.py +210 -0
  73. misterdev/core/verification/critic.py +324 -0
  74. misterdev/core/verification/gatekeeper/__init__.py +631 -0
  75. misterdev/core/verification/gatekeeper/constants.py +138 -0
  76. misterdev/core/verification/gatekeeper/helpers.py +28 -0
  77. misterdev/core/verification/goal_check.py +219 -0
  78. misterdev/core/verification/independent.py +68 -0
  79. misterdev/core/verification/mutation_gate.py +221 -0
  80. misterdev/core/verification/preflight.py +95 -0
  81. misterdev/core/verification/spec_tests.py +175 -0
  82. misterdev/core/verification/validator.py +495 -0
  83. misterdev/core/verification/vision_verify.py +185 -0
  84. misterdev/core/verification/web_verify.py +408 -0
  85. misterdev/environments/__init__.py +0 -0
  86. misterdev/environments/base_env.py +18 -0
  87. misterdev/environments/container_env.py +87 -0
  88. misterdev/environments/venv_env.py +42 -0
  89. misterdev/llm/__init__.py +0 -0
  90. misterdev/llm/client/__init__.py +152 -0
  91. misterdev/llm/client/base.py +382 -0
  92. misterdev/llm/client/edits.py +70 -0
  93. misterdev/llm/client/embeddings.py +121 -0
  94. misterdev/llm/client/errors.py +134 -0
  95. misterdev/llm/client/providers.py +535 -0
  96. misterdev/llm/client/response.py +24 -0
  97. misterdev/llm/prompt_manager.py +82 -0
  98. misterdev/llm/responses/__init__.py +34 -0
  99. misterdev/llm/responses/apply.py +131 -0
  100. misterdev/llm/responses/json_extract.py +80 -0
  101. misterdev/llm/responses/models.py +43 -0
  102. misterdev/llm/responses/parsing.py +494 -0
  103. misterdev/logging_setup.py +20 -0
  104. misterdev/mcp_server.py +208 -0
  105. misterdev/nl_cli.py +149 -0
  106. misterdev/plugins.py +115 -0
  107. misterdev/py.typed +0 -0
  108. misterdev/task_executors/__init__.py +0 -0
  109. misterdev/task_executors/base_executor.py +10 -0
  110. misterdev/task_executors/markdown_plan_executor/__init__.py +90 -0
  111. misterdev/task_executors/markdown_plan_executor/commands_mixin.py +82 -0
  112. misterdev/task_executors/markdown_plan_executor/context_mixin.py +221 -0
  113. misterdev/task_executors/markdown_plan_executor/critic_spec_mixin.py +174 -0
  114. misterdev/task_executors/markdown_plan_executor/edits_mixin.py +251 -0
  115. misterdev/task_executors/markdown_plan_executor/execute_mixin.py +727 -0
  116. misterdev/task_executors/markdown_plan_executor/gates_mixin.py +203 -0
  117. misterdev/task_executors/markdown_plan_executor/git_mixin.py +219 -0
  118. misterdev/task_executors/markdown_plan_executor/helpers.py +521 -0
  119. misterdev/task_executors/markdown_plan_executor/llm_mixin.py +238 -0
  120. misterdev/task_executors/markdown_plan_executor/results_mixin.py +23 -0
  121. misterdev/tools/__init__.py +19 -0
  122. misterdev/tools/base_tool.py +14 -0
  123. misterdev/tools/command.py +75 -0
  124. misterdev/tools/file_io.py +69 -0
  125. misterdev/tools/formatter.py +26 -0
  126. misterdev/tools/git_tool.py +90 -0
  127. misterdev/utils/__init__.py +0 -0
  128. misterdev/utils/file_utils.py +169 -0
  129. misterdev/utils/process.py +23 -0
  130. misterdev-0.2.0.dist-info/METADATA +326 -0
  131. misterdev-0.2.0.dist-info/RECORD +136 -0
  132. misterdev-0.2.0.dist-info/WHEEL +5 -0
  133. misterdev-0.2.0.dist-info/entry_points.txt +3 -0
  134. misterdev-0.2.0.dist-info/licenses/COMMERCIAL_LICENSE.md +34 -0
  135. misterdev-0.2.0.dist-info/licenses/LICENSE +661 -0
  136. misterdev-0.2.0.dist-info/top_level.txt +1 -0
misterdev/cli.py ADDED
@@ -0,0 +1,370 @@
1
+ import argparse
2
+ import sys
3
+ from rich.console import Console
4
+ from rich.markdown import Markdown
5
+ from rich.panel import Panel
6
+ from rich.table import Table
7
+
8
+ from misterdev import __version__
9
+ from misterdev.agent import ProjectOrchestrator
10
+ from misterdev.logging_setup import setup_logger
11
+
12
+ logger = setup_logger("cli")
13
+ console = Console()
14
+
15
+
16
+ def _print_report(project_path: str) -> None:
17
+ """Render the aggregated cost/model/audit summary for a project."""
18
+ from misterdev.core.reporting.report_view import collect
19
+
20
+ data = collect(project_path)
21
+ rpt = data["latest_report"]
22
+ if rpt:
23
+ cost = rpt.get("llm_cost", 0.0) or 0.0
24
+ console.print(
25
+ f"[bold]Latest build[/] — {rpt.get('mode', '?')} on "
26
+ f"{rpt.get('project', '?')} "
27
+ f"[{'green' if rpt.get('validation_passed') else 'red'}]"
28
+ f"{'PASSED' if rpt.get('validation_passed') else 'not green'}[/]"
29
+ )
30
+ console.print(
31
+ f" ${cost:.4f} over {rpt.get('llm_calls', 0)} call(s), "
32
+ f"{rpt.get('llm_tokens', 0):,} tokens · "
33
+ f"completed {len(rpt.get('completed', []))}, "
34
+ f"failed {len(rpt.get('failed', []))}, "
35
+ f"deferred {len(rpt.get('deferred', []))}"
36
+ )
37
+ p_in = rpt.get("llm_prompt_tokens", 0) or 0
38
+ p_out = rpt.get("llm_completion_tokens", 0) or 0
39
+ p_cache = rpt.get("llm_cache_read_tokens", 0) or 0
40
+ if p_in or p_out:
41
+ cached = f", {p_cache:,} cached" if p_cache else ""
42
+ console.print(
43
+ f" [dim]tokens: {p_in:,} input / {p_out:,} output{cached}[/]"
44
+ )
45
+ else:
46
+ console.print("[dim]No saved build report found.[/]")
47
+
48
+ models = data["models"]
49
+ if models:
50
+ table = Table(title="\nModel performance (from the ledger)")
51
+ table.add_column("Model", style="cyan")
52
+ table.add_column("Attempts", justify="right")
53
+ table.add_column("Pass rate", justify="right")
54
+ table.add_column("First-try", justify="right")
55
+ table.add_column("Avg $/success", justify="right")
56
+ for m in models:
57
+ table.add_row(
58
+ m["model"],
59
+ str(m["attempts"]),
60
+ f"{m['success_rate'] * 100:.0f}%",
61
+ f"{m['first_try_rate'] * 100:.0f}%",
62
+ f"${m['avg_cost']:.4f}",
63
+ )
64
+ console.print(table)
65
+ else:
66
+ console.print("[dim]No model ledger yet.[/]")
67
+
68
+ audit = data["audit"]
69
+ if audit["total_events"]:
70
+ cmds = audit["commands"]
71
+ gov = audit["governance"]
72
+ console.print(
73
+ f"\n[bold]Audit trail[/] — {audit['total_events']} event(s): "
74
+ f"{cmds['ok']} command(s) ok / {cmds['failed']} failed, "
75
+ f"{audit['edits']['total']} edit(s)"
76
+ + (
77
+ f", {gov['escalated']} governance escalation(s)"
78
+ if gov["escalated"]
79
+ else ""
80
+ )
81
+ )
82
+ top = sorted(
83
+ audit["edits"]["by_file"].items(), key=lambda kv: kv[1], reverse=True
84
+ )[:5]
85
+ for path, n in top:
86
+ console.print(f" [dim]{n}×[/] {path}")
87
+ else:
88
+ console.print("[dim]No audit trail yet.[/]")
89
+
90
+
91
+ def main():
92
+ # Natural-language mode: when the first argument isn't a known subcommand
93
+ # (and isn't a flag), treat the whole line as plain English and let
94
+ # misterdev's model route it — no flags to memorize. Known subcommands fall
95
+ # through to the flag-based parser below, so power users are unaffected.
96
+ import sys
97
+
98
+ _known = {"scan", "list", "status", "report", "run", "plan", "build"}
99
+ argv = sys.argv[1:]
100
+ if argv and not argv[0].startswith("-") and argv[0] not in _known:
101
+ from misterdev.nl_cli import route
102
+
103
+ sys.exit(route(" ".join(argv), ProjectOrchestrator()))
104
+
105
+ parser = argparse.ArgumentParser(
106
+ description="misterdev — autonomous build orchestrator"
107
+ )
108
+ parser.add_argument(
109
+ "--version", action="version", version=f"misterdev {__version__}"
110
+ )
111
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
112
+
113
+ # 'scan' command
114
+ scan_parser = subparsers.add_parser("scan", help="Scan a directory for projects")
115
+ scan_parser.add_argument("directory", type=str, help="Directory to scan")
116
+
117
+ # 'list' command
118
+ subparsers.add_parser("list", help="List all registered projects")
119
+
120
+ # 'status' command
121
+ status_parser = subparsers.add_parser("status", help="Show status of a project")
122
+ status_parser.add_argument(
123
+ "project_path", type=str, nargs="?", default=".", help="Path to the project"
124
+ )
125
+
126
+ # 'report' command (aggregated cost/audit/model view)
127
+ report_parser = subparsers.add_parser(
128
+ "report",
129
+ help="Summarize cost, model performance, and the audit trail for a project",
130
+ )
131
+ report_parser.add_argument(
132
+ "project_path", type=str, nargs="?", default=".", help="Path to the project"
133
+ )
134
+
135
+ # 'run' command (legacy)
136
+ run_parser = subparsers.add_parser("run", help="Run tasks for a project")
137
+ run_parser.add_argument(
138
+ "project_path",
139
+ type=str,
140
+ nargs="?",
141
+ default=".",
142
+ help="Path to the project (defaults to current dir)",
143
+ )
144
+ run_parser.add_argument("--task", type=str, help="Specific task ID to run")
145
+ run_parser.add_argument(
146
+ "--dry-run",
147
+ action="store_true",
148
+ help="Show execution plan without running tasks",
149
+ )
150
+ run_parser.add_argument(
151
+ "--skip-preflight",
152
+ action="store_true",
153
+ help="Skip pre-flight devplan validation",
154
+ )
155
+ run_parser.add_argument(
156
+ "--force",
157
+ action="store_true",
158
+ help="Re-run all tasks, ignoring the completion cache",
159
+ )
160
+ run_parser.add_argument(
161
+ "--status",
162
+ action="store_true",
163
+ help="Show which tasks would run vs skip, then exit",
164
+ )
165
+
166
+ # 'plan' command (interactive: analyze -> recommend -> compose -> confirm)
167
+ plan_parser = subparsers.add_parser(
168
+ "plan",
169
+ help="Analyze the project, recommend work, and compose a plan interactively",
170
+ )
171
+ plan_parser.add_argument(
172
+ "project_path", type=str, nargs="?", default=".", help="Path to the project"
173
+ )
174
+ plan_parser.add_argument(
175
+ "--budget", type=float, default=100.0, help="Max dollar budget"
176
+ )
177
+ plan_parser.add_argument(
178
+ "--no-rollback",
179
+ action="store_true",
180
+ help="Disable integration-gate revert of regressing tasks",
181
+ )
182
+
183
+ # 'build' command (6-phase workflow from /build skill)
184
+ build_parser = subparsers.add_parser(
185
+ "build", help="Autonomous build/debug/complete workflow"
186
+ )
187
+ build_parser.add_argument(
188
+ "project_path", type=str, nargs="?", default=".", help="Path to the project"
189
+ )
190
+ build_parser.add_argument(
191
+ "prompt",
192
+ nargs="*",
193
+ default=[],
194
+ help="Mode or description (debug, complete, review, new <desc>, or free text)",
195
+ )
196
+ build_parser.add_argument(
197
+ "--budget", type=float, default=100.0, help="Max dollar budget"
198
+ )
199
+ build_parser.add_argument(
200
+ "--commit", action="store_true", help="Commit after each completed task"
201
+ )
202
+ build_parser.add_argument(
203
+ "--no-verify", action="store_true", help="Skip final validation phase"
204
+ )
205
+ build_parser.add_argument(
206
+ "--no-suggest", action="store_true", help="Skip suggest scan"
207
+ )
208
+ build_parser.add_argument(
209
+ "--dry-run", action="store_true", help="Plan only, show tasks without executing"
210
+ )
211
+ build_parser.add_argument(
212
+ "--interactive",
213
+ "-i",
214
+ action="store_true",
215
+ help="Wait for user confirmation between tasks",
216
+ )
217
+ build_parser.add_argument(
218
+ "--parallel", action="store_true", help="Execute independent tasks concurrently"
219
+ )
220
+ build_parser.add_argument(
221
+ "--no-rollback",
222
+ action="store_true",
223
+ help="Disable auto-bisect/revert of a regressing task on gate failure",
224
+ )
225
+ build_parser.add_argument(
226
+ "--focus", type=str, help="Restrict work to specific area"
227
+ )
228
+ build_parser.add_argument(
229
+ "--allow-dirty",
230
+ action="store_true",
231
+ help="Allow building on a working tree with uncommitted changes",
232
+ )
233
+ build_parser.add_argument(
234
+ "--max-tasks",
235
+ type=int,
236
+ default=None,
237
+ help="Cap the number of tasks this run will plan/execute (bounds cost)",
238
+ )
239
+
240
+ args = parser.parse_args()
241
+
242
+ orchestrator = ProjectOrchestrator()
243
+
244
+ if args.command == "scan":
245
+ orchestrator.scan_directory(args.directory)
246
+ console.print("[green]Scan complete.[/]")
247
+ elif args.command == "list":
248
+ projects = orchestrator.list_projects()
249
+ if not projects:
250
+ console.print("[dim]No projects registered.[/]")
251
+ else:
252
+ table = Table(title="Registered Projects")
253
+ table.add_column("Name", style="cyan")
254
+ table.add_column("Path", style="dim")
255
+ for path, info in projects.items():
256
+ table.add_row(info["name"], path)
257
+ console.print(table)
258
+ elif args.command == "status":
259
+ status = orchestrator.get_project_status(args.project_path)
260
+ if "error" in status:
261
+ console.print(f"[red]Error:[/] {status['error']}")
262
+ else:
263
+ console.print(f"[bold]{status['name']}[/] {status['path']}")
264
+ if status["description"]:
265
+ console.print(f"[dim]{status['description']}[/]")
266
+ if not status["tasks"]:
267
+ console.print("\n[dim]No tasks found.[/]")
268
+ else:
269
+ table = Table(title="Tasks")
270
+ table.add_column("ID", style="cyan")
271
+ table.add_column("Status")
272
+ table.add_column("Description", max_width=60)
273
+ for task in status["tasks"]:
274
+ style = {
275
+ "completed": "green",
276
+ "failed": "red",
277
+ "in_progress": "yellow",
278
+ }.get(task["status"], "dim")
279
+ table.add_row(
280
+ task["id"], f"[{style}]{task['status']}[/]", task["description"]
281
+ )
282
+ console.print(table)
283
+ elif args.command == "report":
284
+ _print_report(args.project_path)
285
+ elif args.command == "run":
286
+ if args.task:
287
+ logger.info(
288
+ f"Running specific task {args.task} in project {args.project_path}"
289
+ )
290
+ orchestrator.run_task(args.project_path, args.task)
291
+ else:
292
+ logger.info(f"Running pending tasks for project {args.project_path}")
293
+ orchestrator.run_project(
294
+ args.project_path,
295
+ dry_run=args.dry_run,
296
+ skip_preflight=args.skip_preflight,
297
+ force=args.force,
298
+ status=args.status,
299
+ )
300
+ elif args.command == "build":
301
+ build_args = list(args.prompt)
302
+ if args.budget != 100.0:
303
+ build_args.extend(["--budget", str(args.budget)])
304
+ if args.commit:
305
+ build_args.append("--commit")
306
+ if args.no_verify:
307
+ build_args.append("--no-verify")
308
+ if args.no_suggest:
309
+ build_args.append("--no-suggest")
310
+ if args.dry_run:
311
+ build_args.append("--dry-run")
312
+ if args.interactive:
313
+ build_args.append("--interactive")
314
+ if args.parallel:
315
+ build_args.append("--parallel")
316
+ if args.no_rollback:
317
+ build_args.append("--no-rollback")
318
+ if args.allow_dirty:
319
+ build_args.append("--allow-dirty")
320
+ if args.focus:
321
+ build_args.extend(["--focus", args.focus])
322
+ if args.max_tasks is not None:
323
+ build_args.extend(["--max-tasks", str(args.max_tasks)])
324
+
325
+ report = orchestrator.build(args.project_path, " ".join(build_args))
326
+ console.print("\n")
327
+ if orchestrator.last_build_succeeded:
328
+ console.print(
329
+ Panel(
330
+ Markdown(report),
331
+ title="[bold green]Build Complete[/bold green]",
332
+ expand=False,
333
+ )
334
+ )
335
+ else:
336
+ console.print(
337
+ Panel(
338
+ Markdown(report),
339
+ title="[bold red]Build Failed Validation[/bold red]",
340
+ expand=False,
341
+ )
342
+ )
343
+ sys.exit(1)
344
+ elif args.command == "plan" or args.command is None:
345
+ # Plain `misterdev` (no subcommand) launches interactive planning.
346
+ path = getattr(args, "project_path", ".")
347
+ plan_args = []
348
+ if getattr(args, "budget", 100.0) != 100.0:
349
+ plan_args.extend(["--budget", str(args.budget)])
350
+ if getattr(args, "no_rollback", False):
351
+ plan_args.append("--no-rollback")
352
+ report = orchestrator.interactive_plan(path, " ".join(plan_args))
353
+ console.print("\n")
354
+ title = (
355
+ "[bold green]Build Complete[/bold green]"
356
+ if orchestrator.last_build_succeeded
357
+ else "[bold yellow]Planning Result[/bold yellow]"
358
+ )
359
+ console.print(Panel(Markdown(report), title=title, expand=False))
360
+ else:
361
+ parser.print_help()
362
+ sys.exit(1)
363
+
364
+
365
+ if __name__ == "__main__":
366
+ try:
367
+ main()
368
+ except KeyboardInterrupt:
369
+ console.print("\n[yellow]Interrupted.[/]")
370
+ sys.exit(130)