development-engine-vector 0.3.1__tar.gz → 0.5.1__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 (58) hide show
  1. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/PKG-INFO +26 -16
  2. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/changelog.md +66 -0
  3. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/contributing.md +6 -2
  4. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/__init__.py +1 -1
  5. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/cli/cli.py +201 -45
  6. development_engine_vector-0.5.1/dev/kernel/kernel_core.py +388 -0
  7. development_engine_vector-0.5.1/dev/kernel/otk_kernel.py +274 -0
  8. development_engine_vector-0.5.1/dev/kernel/pmf_kernel.py +24 -0
  9. development_engine_vector-0.5.1/dev/ui/actions.py +111 -0
  10. development_engine_vector-0.5.1/dev/ui/routes.py +298 -0
  11. development_engine_vector-0.5.1/dev/ui/static/web.js +940 -0
  12. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/templates.py +138 -0
  13. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/release.py +2 -2
  14. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/versioning.py +7 -3
  15. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/docs/architecture.md +9 -3
  16. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/pyproject.toml +1 -1
  17. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/readme.md +25 -15
  18. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/tests/test_dev_cli.py +4 -6
  19. development_engine_vector-0.5.1/version +1 -0
  20. development_engine_vector-0.3.1/dev/kernel/pmf_kernel.py +0 -432
  21. development_engine_vector-0.3.1/dev/ui/actions.py +0 -47
  22. development_engine_vector-0.3.1/dev/ui/routes.py +0 -114
  23. development_engine_vector-0.3.1/dev/ui/static/web.js +0 -528
  24. development_engine_vector-0.3.1/version +0 -1
  25. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/.flake8 +0 -0
  26. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/.gitignore +0 -0
  27. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/__main__.py +0 -0
  28. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/cli/__init__.py +0 -0
  29. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/kernel/__init__.py +0 -0
  30. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/kernel/config.py +0 -0
  31. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/kernel/db.py +0 -0
  32. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/kernel/paths.py +0 -0
  33. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/kernel/selfcheck.py +0 -0
  34. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/__init__.py +0 -0
  35. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/__init__.py +0 -0
  36. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/base.py +0 -0
  37. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/checks.py +0 -0
  38. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/events.py +0 -0
  39. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/health.py +0 -0
  40. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/identity.py +0 -0
  41. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/manifest.py +0 -0
  42. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/overview.py +0 -0
  43. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/db/runs.py +0 -0
  44. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/static/__init__.py +0 -0
  45. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/static/web.css +0 -0
  46. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/ui/web.py +0 -0
  47. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/utils.py +0 -0
  48. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/vcs/__init__.py +0 -0
  49. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/vcs/git.py +0 -0
  50. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/vcs/github.py +0 -0
  51. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/__init__.py +0 -0
  52. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/cda.py +0 -0
  53. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/changelog.py +0 -0
  54. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev/workflow/preflight.py +0 -0
  55. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/dev-cli.toml.example +0 -0
  56. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/license +0 -0
  57. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/tests/__init__.py +0 -0
  58. {development_engine_vector-0.3.1 → development_engine_vector-0.5.1}/tests/test_cda_preflight.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: development-engine-vector
3
- Version: 0.3.1
3
+ Version: 0.5.1
4
4
  Summary: Development Engine Vector — internal developer workflow CLI.
5
5
  Author-email: Ernie Butcher <ernie@fiosii.com>
6
6
  License-Expression: MIT
@@ -187,35 +187,45 @@ dev ui status # show pid, started time, log path
187
187
  dev ui restart # restart
188
188
  ```
189
189
 
190
- ### `dev pmf`
190
+ ### `dev otk`
191
191
 
192
- Manage the embedded PMF kernel and background services.
192
+ Manage the **Ops Tool Kernel (OTK)** and background services/tasks.
193
193
 
194
194
  ```bash
195
- dev pmf services # list all services and status
196
- dev pmf status [service_id] # detailed status for one or all services
197
- dev pmf start ui # start a service
198
- dev pmf stop ui # stop a service
199
- dev pmf restart ui # restart a service
200
- dev pmf logs ui [--tail 50] # tail service log
201
- dev pmf up # start all services (called by launchd on login)
202
- dev pmf install # register macOS LaunchAgent (auto-start on login)
203
- dev pmf uninstall # remove LaunchAgent
195
+ dev otk services # list services/tasks and status
196
+ dev otk status [service_id] # detailed status for one or all
197
+ dev otk start ui # start daemon service
198
+ dev otk start preflight --project <path> # run preflight task service
199
+ dev otk start vet # run control vet task service
200
+ dev otk start build --project <path> # run build task service
201
+ dev otk start publish-check # run dry-run publish validation
202
+ dev otk start release-dryrun --project <path> # run full dry-run release workflow
203
+ dev otk stop ui # stop daemon service
204
+ dev otk restart ui # restart daemon service
205
+ dev otk logs vet --tail 100 # tail service/task logs
206
+ dev otk events --tail 20 # inspect kernel event journal
207
+ dev otk up # start default runtime services
208
+ dev otk install # register macOS LaunchAgent (auto-start)
209
+ dev otk uninstall # remove LaunchAgent
204
210
  ```
205
211
 
212
+ `dev pmf ...` remains available as a legacy alias for backward compatibility.
213
+
206
214
  ### `dev setup`
207
215
 
208
216
  Full onboarding wizard — run once after install.
209
217
 
210
218
  ```bash
211
- dev setup # init dirs + pmf install + ui start + open browser
219
+ dev setup # init dirs + otk install + ui start + open browser
212
220
  dev setup --no-browser # same, skip browser
221
+ dev setup --run-otk-chain # also run the OTK preflight → vet chain
213
222
  ```
214
223
 
215
224
  Steps:
216
225
  1. **Init** — create `~/Library/goCosmix/tools/dev/` and all runtime subdirs; patch `~/.zprofile` if `dev` is not on PATH
217
- 2. **PMF install** — register `com.gocosmix.dev` LaunchAgent so `dev` starts automatically on every login
218
- 3. **Up** — start the web UI via PMF kernel, open browser at `http://127.0.0.1:9001`
226
+ 2. **OTK install** — register `com.gocosmix.dev` LaunchAgent so `dev` starts automatically on every login
227
+ 3. **Up** — start the web UI via OTK kernel, open browser at `http://127.0.0.1:9001`
228
+ 4. **Phase 2** — optionally run `preflight → vet` against the dev source tree for a fresh post-setup validation pass
219
229
 
220
230
  ## ⚙️ Configuration
221
231
 
@@ -251,7 +261,7 @@ dev --config ./custom.dev-cli.toml release --project /Volumes/intel/systems/cda/
251
261
 
252
262
  ```
253
263
  dev/
254
- ├── kernel/ — runtime foundation (paths, PMF kernel, DB, config, self-check)
264
+ ├── kernel/ — runtime foundation (OTK kernel, shared core, paths, DB, config, self-check)
255
265
  ├── workflow/ — business logic (versioning, changelog, preflight, release)
256
266
  ├── vcs/ — version control ops (git, GitHub API)
257
267
  ├── ui/ — embedded WSGI dashboard (port 9001, indigo theme)
@@ -5,6 +5,72 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.1] - 2026-05-12
9
+
10
+ ### Fixed
11
+ - UI service status stuck on `"starting"` — `_refresh()` now advances to `"running"` and persists state when the process is confirmed alive
12
+ - `VersionManager.get_sync_targets()` removed stale `vscode_ark/__init__.py` and `setup.py` targets; now resolves `dev/__init__.py` from the project tree
13
+ - `ReleaseOrchestrator.commit_and_tag()` staged `vscode_ark/__init__.py` and `setup.py` (copy-paste from cda); corrected to `dev/__init__.py`
14
+ - `get_recent_runs` unused import in `routes.py` removed (lint failure)
15
+ - Release page showed always-empty kernel run history; now shows real action states (pass/fail, exit code, timestamps)
16
+
17
+ ### Added
18
+ - `/api/kernel/logs?service=X&lines=N` endpoint — tails any service log file from the UI
19
+ - OTK page **Logs** button per service opens a modal with the last 100 log lines
20
+ - Action buttons now poll to completion and display a result toast with exit code and captured output
21
+ - Raw Query page has a database selector: `control.db` (vet history) or `dev.db` (kernel/projects)
22
+ - `sync` OTK task service added — bootstrap/editable-install from the OTK page
23
+ - Setup page shows `dev otk install` guidance when the LaunchAgent is not installed
24
+ - Git page shows "Clean — no commits since vX.Y.Z" on a clean repo instead of a blank panel
25
+ - `vet.py` writes `vet.start` / `vet.pass` / `vet.fail` events to the `events` table and refreshes `identity.version` on every passing run
26
+
27
+ ## [0.5.0] - 2026-05-11
28
+
29
+ ### Added
30
+ - **Full UI expansion** — 7 new dashboard pages surfacing the entire `dev` command surface:
31
+ - **OTK** — live service/task table with Start / Stop / Restart controls per service
32
+ - **Selfcheck** — editable install path, DB state, tool availability, dependency status
33
+ - **Version** — version file vs package version, sync target map
34
+ - **Release** — release run history, registered project registry, one-click workflow buttons (Preflight, Vet, Build, Publish Check, Release Dry-Run)
35
+ - **Project** — project dir, config file, package/changelog sync state
36
+ - **Git** — branch, last tag, dirty state, remote URL, commits since tag
37
+ - **Setup** — DEV_HOME, launch agent install status, all runtime path table
38
+ - New backend summary APIs: `/api/dev/selfcheck`, `/api/dev/version`, `/api/dev/changelog`, `/api/dev/git`, `/api/dev/project`, `/api/dev/release`, `/api/dev/setup`
39
+ - Background action runner now distinguishes task vs daemon services: tasks run synchronously to completion and capture full output before updating action state
40
+ - Navigation expanded with new groups: OTK under Core; Selfcheck/Version/Release under Config; Project/Git under Inventory; Setup under System
41
+
42
+ ## [0.4.1] - 2026-05-11
43
+
44
+ ### Added
45
+ - Optional Phase 2 onboarding flow: `dev setup --run-otk-chain` runs the OTK preflight → vet task chain after startup
46
+ - Dashboard now renders the OTK kernel brain directly on the default page:
47
+ - runtime service table
48
+ - recent kernel event feed
49
+ - Added synchronous task execution support in the shared kernel core for onboarding and orchestration
50
+
51
+ ### Changed
52
+ - Dashboard overview now includes kernel-state visibility in addition to vet stats
53
+ - OTK task events are recorded in the runtime journal and surfaced through the UI
54
+
55
+ ## [0.4.0] - 2026-05-11
56
+
57
+ ### Added
58
+ - **Ops Tool Kernel (OTK)** architecture for `dev`:
59
+ - `kernel_core.py` shared kernel base (service lifecycle, persistent runtime state, event journal)
60
+ - `otk_kernel.py` system-adapted kernel (daemon + task service catalog)
61
+ - `pmf_kernel.py` compatibility shim preserving PMF imports
62
+ - New OTK task services: `preflight`, `vet`, `build`, `publish-check`, `release-dryrun`
63
+ - New runtime events command: `dev otk events` (also available through legacy `dev pmf events`)
64
+ - New web API endpoints for kernel state and actions:
65
+ - `GET /api/kernel/services`, `GET /api/kernel/status`, `GET /api/kernel/events`
66
+ - `POST /api/kernel/start`, `POST /api/kernel/stop`, `POST /api/kernel/restart`
67
+ - `GET /api/actions`, `GET /api/action/status`
68
+
69
+ ### Changed
70
+ - LaunchAgent now calls `dev otk up` (legacy `dev pmf ...` remains as an alias)
71
+ - `dev setup` language updated from PMF-first to OTK-first flow
72
+ - Control data seed metadata updated for PyPI package name: `development-engine-vector`
73
+
8
74
  ## [0.3.1] - 2026-05-11
9
75
 
10
76
  ### Changed
@@ -87,7 +87,9 @@ dev/
87
87
  │ ├── dev/ # Python package
88
88
  │ │ ├── kernel/ # runtime foundation
89
89
  │ │ │ ├── paths.py # DEV_HOME = ~/Library/goCosmix/tools/dev
90
- │ │ │ ├── pmf_kernel.py # PMFKernel, launchd integration (port 9001)
90
+ │ │ │ ├── kernel_core.py # shared kernel DNA (service lifecycle + events)
91
+ │ │ │ ├── otk_kernel.py # Ops Tool Kernel (dev-specific services/tasks)
92
+ │ │ │ ├── pmf_kernel.py # compatibility shim over OTK symbols
91
93
  │ │ │ ├── db.py # WAL SQLite — runs + projects
92
94
  │ │ │ ├── config.py # DevConfig — reads .dev-cli.toml
93
95
  │ │ │ └── selfcheck.py # engine self-diagnostics
@@ -128,7 +130,9 @@ Thin click wrappers. All logic delegates to `workflow/` and `vcs/` modules.
128
130
 
129
131
  ### Kernel (`dev/kernel/`)
130
132
  - `paths.py` — canonical path constants; `DEV_HOME = ~/Library/goCosmix/tools/dev`
131
- - `pmf_kernel.py` — `PMFKernel`: start/stop/restart/status for background services; `install_launchd` / `uninstall_launchd` for `com.gocosmix.dev` LaunchAgent
133
+ - `kernel_core.py` — shared kernel base (`ServiceSpec`, lifecycle, state persistence, event journal)
134
+ - `otk_kernel.py` — `OTKKernel`: Ops Tool Kernel service/task catalog (`ui`, `preflight`, `vet`, `build`, `publish-check`, `release-dryrun`) and launchd integration
135
+ - `pmf_kernel.py` — backward-compatible alias layer for existing PMF imports
132
136
  - `db.py` — WAL SQLite; `record_run`, `finish_run`, `upsert_project`, `get_recent_runs`
133
137
  - `config.py` — loads `.dev-cli.toml` / `dev-cli.toml` from project root
134
138
  - `selfcheck.py` — five checks: version, install path, DB, tools, dependencies
@@ -1,3 +1,3 @@
1
1
  """dev — Development Engine Vector."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.5.0"
@@ -10,8 +10,8 @@ import click
10
10
 
11
11
  from dev.kernel.config import DevConfig
12
12
  from dev.kernel.paths import ensure_dirs
13
- from dev.kernel.pmf_kernel import (
14
- PMFKernel, PMFKernelError,
13
+ from dev.kernel.otk_kernel import (
14
+ OTKKernel, OTKKernelError,
15
15
  install_launchd, uninstall_launchd, plist_path,
16
16
  wait_for_port_and_open_browser,
17
17
  )
@@ -272,7 +272,7 @@ def selfcheck():
272
272
  print(f" {result['passed']} passed, {result['failed']} failed")
273
273
 
274
274
 
275
- _kernel = PMFKernel()
275
+ _kernel = OTKKernel()
276
276
 
277
277
 
278
278
  # ── helper ────────────────────────────────────────────────────────────────────
@@ -300,7 +300,7 @@ def _hr():
300
300
 
301
301
  def _pmf_warn_if_not_installed():
302
302
  if not plist_path().exists():
303
- click.echo(_yellow(" ⚠ LaunchAgent not installed — run `dev pmf install` to auto-start on login."))
303
+ click.echo(_yellow(" ⚠ LaunchAgent not installed — run `dev otk install` (or `dev pmf install`) to auto-start on login."))
304
304
 
305
305
 
306
306
  # ── ui group ─────────────────────────────────────────────────────────────────
@@ -317,7 +317,7 @@ def ui():
317
317
  @click.option("--no-browser", "no_browser", is_flag=True, default=False,
318
318
  help="Don't open browser automatically")
319
319
  def ui_start(host, port, no_browser):
320
- """Start the web UI via PMF."""
320
+ """Start the web UI via OTK."""
321
321
  _pmf_warn_if_not_installed()
322
322
  try:
323
323
  result = _kernel.start_service("ui", options={"host": host, "port": port})
@@ -326,7 +326,7 @@ def ui_start(host, port, no_browser):
326
326
  url = f"http://{host}:{port}"
327
327
  click.echo(_dim(" Opening browser when server is ready..."))
328
328
  wait_for_port_and_open_browser(url, host, port)
329
- except PMFKernelError as exc:
329
+ except OTKKernelError as exc:
330
330
  click.echo(_red(f" {exc}"))
331
331
 
332
332
 
@@ -336,7 +336,7 @@ def ui_stop():
336
336
  try:
337
337
  result = _kernel.stop_service("ui")
338
338
  click.echo(green(f" Stopped {result['label']}"))
339
- except PMFKernelError as exc:
339
+ except OTKKernelError as exc:
340
340
  click.echo(_red(f" {exc}"))
341
341
 
342
342
 
@@ -367,7 +367,7 @@ def ui_restart(host, port, no_browser):
367
367
  url = f"http://{host}:{port}"
368
368
  click.echo(_dim(" Opening browser when server is ready..."))
369
369
  wait_for_port_and_open_browser(url, host, port)
370
- except PMFKernelError as exc:
370
+ except OTKKernelError as exc:
371
371
  click.echo(_red(f" {exc}"))
372
372
 
373
373
 
@@ -375,16 +375,16 @@ def ui_restart(host, port, no_browser):
375
375
 
376
376
  @cli.group()
377
377
  def pmf():
378
- """Manage the embedded PMF kernel and dev services."""
378
+ """Legacy alias for OTK commands (kept for backward compatibility)."""
379
379
  pass
380
380
 
381
381
 
382
382
  @pmf.command("services")
383
383
  def pmf_services():
384
- """List PMF services and runtime status."""
384
+ """List OTK services and runtime status."""
385
385
  rows = _kernel.services()
386
386
  click.echo()
387
- click.echo(_bold(" PMF Runtime Services"))
387
+ click.echo(_bold(" OTK Runtime Services"))
388
388
  click.echo(_hr())
389
389
  for svc in rows:
390
390
  status_str = green(svc["status"]) if svc["status"] == "running" else _yellow(svc["status"])
@@ -396,7 +396,7 @@ def pmf_services():
396
396
  @pmf.command("status")
397
397
  @click.argument("service_id", required=False)
398
398
  def pmf_status(service_id):
399
- """Show PMF runtime status for one or all services."""
399
+ """Show OTK runtime status for one or all services."""
400
400
  if service_id:
401
401
  try:
402
402
  svc = _kernel.service_status(service_id)
@@ -407,7 +407,7 @@ def pmf_status(service_id):
407
407
  click.echo(f" Started: {svc['started_at'] or '—'}")
408
408
  click.echo(f" Log: {svc['log_file'] or '—'}")
409
409
  click.echo()
410
- except PMFKernelError as exc:
410
+ except OTKKernelError as exc:
411
411
  click.echo(_red(f" {exc}"))
412
412
  else:
413
413
  ctx = click.get_current_context()
@@ -418,10 +418,15 @@ def pmf_status(service_id):
418
418
  @click.argument("service_id")
419
419
  @click.option("--host", default="127.0.0.1", help="Host override for UI service")
420
420
  @click.option("--port", default=9001, help="Port override for UI service")
421
+ @click.option("--project", default=".", help="Project path override for task services")
421
422
  @click.option("--no-browser", "no_browser", is_flag=True, default=False)
422
- def pmf_start(service_id, host, port, no_browser):
423
- """Start a PMF-managed service."""
424
- options = {"host": host, "port": port} if service_id == "ui" else None
423
+ def pmf_start(service_id, host, port, project, no_browser):
424
+ """Start an OTK-managed service."""
425
+ options = {
426
+ "host": host,
427
+ "port": port,
428
+ "project": str(Path(project).resolve()),
429
+ }
425
430
  try:
426
431
  result = _kernel.start_service(service_id, options=options)
427
432
  click.echo(green(f" Started {result['label']} pid={result['pid']}"))
@@ -429,29 +434,35 @@ def pmf_start(service_id, host, port, no_browser):
429
434
  url = f"http://{host}:{port}"
430
435
  click.echo(_dim(" Opening browser when server is ready..."))
431
436
  wait_for_port_and_open_browser(url, host, port)
432
- except PMFKernelError as exc:
437
+ except OTKKernelError as exc:
433
438
  click.echo(_red(f" {exc}"))
434
439
 
435
440
 
436
441
  @pmf.command("stop")
437
442
  @click.argument("service_id")
438
443
  def pmf_stop(service_id):
439
- """Stop a PMF-managed service."""
444
+ """Stop an OTK-managed service."""
440
445
  try:
441
446
  result = _kernel.stop_service(service_id)
442
447
  click.echo(green(f" Stopped {result['label']}"))
443
- except PMFKernelError as exc:
448
+ except OTKKernelError as exc:
444
449
  click.echo(_red(f" {exc}"))
445
450
 
446
451
 
447
452
  @pmf.command("restart")
448
453
  @click.argument("service_id")
449
- def pmf_restart(service_id):
450
- """Restart a PMF-managed service."""
454
+ @click.option("--host", default="127.0.0.1", help="Host override for UI service")
455
+ @click.option("--port", default=9001, help="Port override for UI service")
456
+ @click.option("--project", default=".", help="Project path override for task services")
457
+ def pmf_restart(service_id, host, port, project):
458
+ """Restart an OTK-managed service."""
451
459
  try:
452
- result = _kernel.restart_service(service_id)
460
+ result = _kernel.restart_service(
461
+ service_id,
462
+ options={"host": host, "port": port, "project": str(Path(project).resolve())},
463
+ )
453
464
  click.echo(green(f" Restarted {result['label']} pid={result['pid']}"))
454
- except PMFKernelError as exc:
465
+ except OTKKernelError as exc:
455
466
  click.echo(_red(f" {exc}"))
456
467
 
457
468
 
@@ -459,20 +470,35 @@ def pmf_restart(service_id):
459
470
  @click.argument("service_id")
460
471
  @click.option("--tail", default=50, show_default=True, help="Lines to tail")
461
472
  def pmf_logs(service_id, tail):
462
- """Display the last lines from a PMF service log."""
473
+ """Display the last lines from an OTK service log."""
463
474
  try:
464
475
  output = _kernel.tail_log(service_id, lines=tail)
465
476
  click.echo(output)
466
- except PMFKernelError as exc:
477
+ except OTKKernelError as exc:
467
478
  click.echo(_red(f" {exc}"))
468
479
 
469
480
 
481
+ @pmf.command("events")
482
+ @click.option("--tail", default=20, show_default=True, help="Number of recent kernel events")
483
+ def pmf_events(tail):
484
+ """Show recent OTK kernel runtime events."""
485
+ events = _kernel.recent_events(limit=tail)
486
+ click.echo()
487
+ click.echo(_bold(" OTK Kernel Events"))
488
+ click.echo(_hr())
489
+ if not events:
490
+ click.echo(_dim(" (no events)"))
491
+ for ev in events:
492
+ click.echo(f" {ev.get('ts', '—')} {_bold(ev.get('kind', 'event')):<18} {ev.get('service_id', '—'):<14} {ev.get('detail', '')}")
493
+ click.echo()
494
+
495
+
470
496
  @pmf.command("up")
471
497
  @click.option("--host", default="127.0.0.1", show_default=True)
472
498
  @click.option("--port", default=9001, show_default=True)
473
499
  @click.option("--no-browser", "no_browser", is_flag=True, default=False)
474
500
  def pmf_up(host, port, no_browser):
475
- """Start all dev services (web UI). Called automatically by launchd on login."""
501
+ """Start default OTK services (web UI). Called by launchd on login."""
476
502
  url = f"http://{host}:{port}"
477
503
  click.echo(_bold(" Development Engine Vector — starting services"))
478
504
  click.echo(_hr())
@@ -483,7 +509,7 @@ def pmf_up(host, port, no_browser):
483
509
  if not no_browser:
484
510
  click.echo(_dim(" Opening browser when server is ready..."))
485
511
  wait_for_port_and_open_browser(url, host, port)
486
- except PMFKernelError as exc:
512
+ except OTKKernelError as exc:
487
513
  click.echo(_yellow(f" Web UI {exc}"))
488
514
 
489
515
  click.echo()
@@ -503,9 +529,10 @@ def pmf_install():
503
529
  click.echo(green(" Loaded: yes — dev will start automatically on next login"))
504
530
  click.echo()
505
531
  click.echo(_dim(" To start services now without logging out:"))
506
- click.echo(_dim(" dev pmf up"))
532
+ click.echo(_dim(" dev otk up"))
533
+ click.echo(_dim(" (legacy alias: dev pmf up)"))
507
534
  click.echo()
508
- except PMFKernelError as exc:
535
+ except OTKKernelError as exc:
509
536
  click.echo(_red(f" {exc}"))
510
537
  click.echo(_yellow(" Make sure `dev` is on PATH: export PATH=\"$HOME/Library/Python/3.9/bin:$PATH\""))
511
538
  click.echo()
@@ -523,29 +550,134 @@ def pmf_uninstall():
523
550
  click.echo(green(" dev will no longer start automatically on login."))
524
551
 
525
552
 
553
+ @cli.group()
554
+ def otk():
555
+ """Manage the Ops Tool Kernel (OTK) and dev runtime services."""
556
+ pass
557
+
558
+
559
+ @otk.command("services")
560
+ @click.pass_context
561
+ def otk_services(ctx):
562
+ """List OTK services and runtime status."""
563
+ ctx.invoke(pmf_services)
564
+
565
+
566
+ @otk.command("status")
567
+ @click.argument("service_id", required=False)
568
+ @click.pass_context
569
+ def otk_status(ctx, service_id):
570
+ """Show OTK runtime status for one or all services."""
571
+ ctx.invoke(pmf_status, service_id=service_id)
572
+
573
+
574
+ @otk.command("start")
575
+ @click.argument("service_id")
576
+ @click.option("--host", default="127.0.0.1", help="Host override for UI service")
577
+ @click.option("--port", default=9001, help="Port override for UI service")
578
+ @click.option("--project", default=".", help="Project path override for task services")
579
+ @click.option("--no-browser", "no_browser", is_flag=True, default=False)
580
+ @click.pass_context
581
+ def otk_start(ctx, service_id, host, port, project, no_browser):
582
+ """Start an OTK-managed service or task."""
583
+ ctx.invoke(
584
+ pmf_start,
585
+ service_id=service_id,
586
+ host=host,
587
+ port=port,
588
+ project=project,
589
+ no_browser=no_browser,
590
+ )
591
+
592
+
593
+ @otk.command("stop")
594
+ @click.argument("service_id")
595
+ @click.pass_context
596
+ def otk_stop(ctx, service_id):
597
+ """Stop an OTK-managed service."""
598
+ ctx.invoke(pmf_stop, service_id=service_id)
599
+
600
+
601
+ @otk.command("restart")
602
+ @click.argument("service_id")
603
+ @click.option("--host", default="127.0.0.1", help="Host override for UI service")
604
+ @click.option("--port", default=9001, help="Port override for UI service")
605
+ @click.option("--project", default=".", help="Project path override for task services")
606
+ @click.pass_context
607
+ def otk_restart(ctx, service_id, host, port, project):
608
+ """Restart an OTK-managed service or task."""
609
+ ctx.invoke(pmf_restart, service_id=service_id, host=host, port=port, project=project)
610
+
611
+
612
+ @otk.command("logs")
613
+ @click.argument("service_id")
614
+ @click.option("--tail", default=50, show_default=True, help="Lines to tail")
615
+ @click.pass_context
616
+ def otk_logs(ctx, service_id, tail):
617
+ """Tail logs for an OTK service or task."""
618
+ ctx.invoke(pmf_logs, service_id=service_id, tail=tail)
619
+
620
+
621
+ @otk.command("events")
622
+ @click.option("--tail", default=20, show_default=True, help="Number of recent kernel events")
623
+ @click.pass_context
624
+ def otk_events(ctx, tail):
625
+ """Show recent OTK events."""
626
+ ctx.invoke(pmf_events, tail=tail)
627
+
628
+
629
+ @otk.command("up")
630
+ @click.option("--host", default="127.0.0.1", show_default=True)
631
+ @click.option("--port", default=9001, show_default=True)
632
+ @click.option("--no-browser", "no_browser", is_flag=True, default=False)
633
+ @click.pass_context
634
+ def otk_up(ctx, host, port, no_browser):
635
+ """Start default OTK services (web UI)."""
636
+ ctx.invoke(pmf_up, host=host, port=port, no_browser=no_browser)
637
+
638
+
639
+ @otk.command("install")
640
+ @click.pass_context
641
+ def otk_install(ctx):
642
+ """Install OTK launchd registration for auto-start on login."""
643
+ ctx.invoke(pmf_install)
644
+
645
+
646
+ @otk.command("uninstall")
647
+ @click.pass_context
648
+ def otk_uninstall(ctx):
649
+ """Uninstall OTK launchd registration."""
650
+ ctx.invoke(pmf_uninstall)
651
+
652
+
526
653
  # ── setup ─────────────────────────────────────────────────────────────────────
527
654
 
528
655
  @cli.command("setup")
529
656
  @click.option("--no-browser", "no_browser", is_flag=True, default=False,
530
657
  help="Don't open browser when the web UI starts")
531
- def setup(no_browser):
658
+ @click.option("--run-otk-chain/--skip-otk-chain", default=False,
659
+ help="Run the first OTK task chain (preflight → vet) after startup")
660
+ def setup(no_browser, run_otk_chain):
532
661
  """
533
- Full onboarding in three steps: init → pmf install → up.
662
+ Full onboarding in three steps: init → otk install → up.
534
663
 
535
664
  \b
536
- Run this once after `pip install dev-cli`.
537
- If `dev` isn't on PATH yet, use the fallback:
665
+ 3. Up — start the web UI via OTK, open browser
538
666
 
539
- python3 -m dev setup
667
+ Optional phase 2:
668
+ - Run the first OTK task chain (preflight → vet) against the dev source tree.
669
+ If `dev` isn't on PATH yet, use the fallback:
670
+ All processes are managed by the OTK kernel. The LaunchAgent calls
671
+ `dev otk up` on every login — no manual interaction needed after setup.
540
672
 
541
673
  \b
542
674
  What each step does:
543
675
  1. Init — create ~/Library/goCosmix/tools/dev/, patch PATH
544
676
  2. Install — register a macOS LaunchAgent so dev starts on every login
545
- 3. Up — start the web UI via PMF, open browser
677
+ 3. Up — start the web UI via OTK, open browser
546
678
 
547
- All processes are managed by the PMF kernel. The LaunchAgent calls
548
- `dev pmf up` on every login — no manual interaction needed after setup.
679
+ All processes are managed by the OTK kernel. The LaunchAgent calls
680
+ `dev otk up` on every login — no manual interaction needed after setup.
549
681
  """
550
682
  import os as _os
551
683
  import shutil as _shutil
@@ -569,7 +701,9 @@ def setup(no_browser):
569
701
  click.echo(_dim(" Three steps to a fully operational dev installation:"))
570
702
  click.echo(_dim(f" 1. Init — create {DEV_HOME}"))
571
703
  click.echo(_dim(" 2. Install — register macOS LaunchAgent (auto-start on login)"))
572
- click.echo(_dim(" 3. Up — start web UI via PMF, open browser"))
704
+ click.echo(_dim(" 3. Up — start web UI via OTK, open browser"))
705
+ if run_otk_chain:
706
+ click.echo(_dim(" 4. Phase 2 — run OTK preflight → vet task chain"))
573
707
  click.echo()
574
708
 
575
709
  # ── Step 1: Init ─────────────────────────────────────────────
@@ -616,13 +750,13 @@ def setup(no_browser):
616
750
 
617
751
  click.echo()
618
752
 
619
- # ── Step 2: PMF install ──────────────────────────────────────
753
+ # ── Step 2: OTK install ──────────────────────────────────────
620
754
  click.echo(_bold(bar))
621
- click.echo(_bold(" Step 2/3 — PMF install"))
755
+ click.echo(_bold(" Step 2/3 — OTK install"))
622
756
  click.echo(_bold(bar))
623
757
  click.echo()
624
758
  click.echo(_dim(" The LaunchAgent registers dev with macOS launchd. On every login,"))
625
- click.echo(_dim(" launchd calls `dev pmf up` — starts the web UI via PMF kernel."))
759
+ click.echo(_dim(" launchd calls `dev otk up` — starts the web UI via OTK kernel."))
626
760
  click.echo(_dim(" No terminal required after this."))
627
761
  click.echo()
628
762
 
@@ -632,9 +766,9 @@ def setup(no_browser):
632
766
  click.echo(f" {green('✓')} LaunchAgent: {target}")
633
767
  click.echo(f" {green('✓')} Loaded — dev starts automatically on every login")
634
768
  pmf_ok = True
635
- except PMFKernelError as exc:
769
+ except OTKKernelError as exc:
636
770
  click.echo(f" {_yellow('⚠')} LaunchAgent registration failed: {exc}")
637
- click.echo(_yellow(" Fix PATH then run `dev pmf install` to retry."))
771
+ click.echo(_yellow(" Fix PATH then run `dev otk install` (or `dev pmf install`) to retry."))
638
772
  click.echo()
639
773
 
640
774
  # ── Step 3: Up ───────────────────────────────────────────────
@@ -651,9 +785,31 @@ def setup(no_browser):
651
785
  if not no_browser:
652
786
  click.echo(_dim(" Opening browser when server is ready..."))
653
787
  wait_for_port_and_open_browser(url, host, port)
654
- except PMFKernelError as exc:
788
+ except OTKKernelError as exc:
655
789
  click.echo(f" {_yellow('⚠')} Web UI: {exc}")
656
- click.echo(_dim(" Try `dev ui start` once PMF is installed."))
790
+ click.echo(_dim(" Try `dev ui start` once OTK is installed."))
791
+
792
+ if run_otk_chain:
793
+ source_root = Path(__file__).resolve().parents[2]
794
+ click.echo()
795
+ click.echo(_bold(bar))
796
+ click.echo(_bold(" Phase 2 — OTK task chain"))
797
+ click.echo(_bold(bar))
798
+ click.echo()
799
+
800
+ try:
801
+ preflight_result = _kernel.run_task("preflight", options={"project": str(source_root)}, timeout=600)
802
+ preflight_status = green("✓") if preflight_result["returncode"] == 0 else _yellow("⚠")
803
+ click.echo(f" {preflight_status} preflight exit={preflight_result['returncode']}")
804
+ except OTKKernelError as exc:
805
+ click.echo(f" {_yellow('⚠')} preflight: {exc}")
806
+
807
+ try:
808
+ vet_result = _kernel.run_task("vet", timeout=900)
809
+ vet_status = green("✓") if vet_result["returncode"] == 0 else _yellow("⚠")
810
+ click.echo(f" {vet_status} vet exit={vet_result['returncode']}")
811
+ except OTKKernelError as exc:
812
+ click.echo(f" {_yellow('⚠')} vet: {exc}")
657
813
 
658
814
  click.echo()
659
815
  click.echo(_bold(BAR))