Skip to content

feat(cli): add daemonless gortex analyze command#84

Open
avfirsov wants to merge 2 commits into
zzet:mainfrom
avfirsov:feat/cli-analyze
Open

feat(cli): add daemonless gortex analyze command#84
avfirsov wants to merge 2 commits into
zzet:mainfrom
avfirsov:feat/cli-analyze

Conversation

@avfirsov

Copy link
Copy Markdown
Contributor

What

Adds a daemonless gortex analyze CLI command:

gortex analyze --kind <synthesizers|resolution_outcomes> --path <dir> [--format json|text]

It indexes --path entirely in-process — no daemon, no socket — and runs the analyzer against the fresh graph. Useful for CI pipelines and one-shot scripts that want graph analytics without standing up the daemon.

Why this shape

To avoid duplicating logic between the MCP tool and the new CLI, the analyzer cores are extracted into a new internal/analyzer package as pure calculations (graph in, struct out):

  • AnalyzeSynthesizers
  • AnalyzeResolutionOutcomes / ClassifyUnresolved

The existing MCP handlers (handleAnalyzeSynthesizers, handleAnalyzeResolutionOutcomes) are refactored to call these cores. Output shapes are unchanged — the existing MCP-layer tests pass as-is, which is the safety net for the refactor. The resolution-outcome taxonomy constants now live in internal/analyzer, with thin aliases retained in the mcp package so existing call sites/tests keep compiling.

Tests

  • internal/analyzer: unit/shape tests for both cores (synthesizer grouping + name filter; the full unresolved-edge taxonomy + reason filter).
  • cmd/gortex: end-to-end index→analyze tests asserting valid JSON for both kinds, plus unsupported-kind validation.
  • Existing internal/mcp analyze handler tests unchanged and green.

go build ./... and go test ./internal/analyzer/... ./cmd/gortex/... ./internal/mcp/... pass.

Notes / scope

  • Two kinds to start (synthesizers, resolution_outcomes) — the ones whose cores are extracted here. Easy to extend the dispatcher to more kinds in follow-ups.
  • Purely additive on the CLI surface; the only change to existing code is the DRY refactor of the two MCP handlers onto the shared cores.

🤖 Generated with Claude Code

avfirsov and others added 2 commits June 13, 2026 10:21
Expose graph analyzers on the CLI without a running daemon. `gortex analyze
--kind <k> --path <dir> [--format json|text]` indexes the path entirely
in-process (no daemon, no socket) and runs the analyzer against the fresh
graph — handy for CI pipelines and one-shot scripts.

Supported kinds: `synthesizers`, `resolution_outcomes`.

To avoid duplicating logic between the MCP tool and the CLI, the analyzer
cores are extracted into a new `internal/analyzer` package as pure
calculations (graph in, struct out):
- `AnalyzeSynthesizers`
- `AnalyzeResolutionOutcomes` / `ClassifyUnresolved`

The existing MCP handlers (`handleAnalyzeSynthesizers`,
`handleAnalyzeResolutionOutcomes`) are refactored to call these cores; their
JSON/compact output shapes are unchanged and the existing MCP-layer tests
pass as-is. The resolution-outcome taxonomy constants now live in
`internal/analyzer` with thin aliases kept in the mcp package.

Tests: unit tests for both cores + an end-to-end index→analyze CLI test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
golangci-lint errcheck flagged the text-format print paths in
cmd/gortex/analyze.go. Ignore the return explicitly, matching the
codebase idiom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@avfirsov avfirsov marked this pull request as ready for review June 14, 2026 08:44
@avfirsov

avfirsov commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Hi @zzet 👋 — gentle ping on these four, all ready for review (CI green, mergeable: CLEAN, no conflicts with main):

Update on the overlap I flagged earlier: I checked #85 and #87 against the temporal work that already landed in main, and there's no real duplication — they're complementary:

No rush at all — happy to split or adjust anything further if you'd prefer. Thanks for the project!

@avfirsov

Copy link
Copy Markdown
Contributor Author

Hi @zzet 👋 — overview of the current open batch (all rebased onto latest main, CI green or running, no conflicts between a PR and main):

Docs / infra (independent, low-risk):

Temporal dispatch resolvers (Go) — these all touch temporal_calls.go/temporal tests, so they conflict with each other; happy to keep rebasing as you merge, just merge in any order and ping me (or tell me a preferred order):

Java cross-language:

LLM:

CLI:

Suggested low-friction start: #94, #92, #88 are the most independent. The temporal-resolver cluster (#87/#89/#90/#98) I'll rebase-on-merge so each lands clean. No rush — thanks for the project!

@avfirsov

Copy link
Copy Markdown
Contributor Author

@zzet — rebase sweep done. After #87 merged (and then #93 change_contract + #95 deps), the temporal-resolver cluster went DIRTY. All affected branches are now rebased onto latest main (2c3530e), with go build ./... + targeted tests (resolver, parser/languages, indexer; mcp for #96) green, and GitHub now reports them MERGEABLE:

#84 / #88 / #92 / #94 needed no rebase (#92 verified clean via git merge-tree). The cluster still self-conflicts on merge — happy to rebase the remainder again whenever you land the next one.

@avfirsov

Copy link
Copy Markdown
Contributor Author

Friendly ping @zzet 🙂 — CI is green (10/10) and the branch is MERGEABLE / CLEAN (no conflicts, not behind main). This adds a daemonless gortex analyze command. Happy to rebase or address any review feedback whenever you have a moment. Thanks!

@zzet

zzet commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Hey @avfirsov
I'm concerned about explicit daemon less mode and I haven't finish experimenting with an alternative resolution of the problem. Can you bring more light on cases you're facing?

@avfirsov

Copy link
Copy Markdown
Contributor Author

Thanks @zzet — and totally fair to hold off if you're still exploring an alternative. Let me lay out the concrete cases that pushed me to a daemonless one-shot, so we're optimizing for the same problem (the capability, not necessarily an explicit user-facing "daemonless mode").

The original need: comparing fork vs. original on a large legacy repo. The whole thing started because I wanted a clean A/B — "does the temporal work actually resolve more edges than baseline?" — on a real legacy codebase. Three constraints made the resident daemon awkward there:

  1. Windows, no WSL, driven from an MCP client (opencode). On native Windows the daemon/AF_UNIX-socket path is the rough edge: running two builds side by side means two sockets, two state homes, and two MCP servers registered in the client. A one-shot analyze that indexes the path in-process, runs the analyzer, prints JSON and exits sidesteps all of it — no socket, no daemon, no collision.

  2. One-shot / CI-style invocation. "Index this path once, give me temporal_orphans / synthesizers as JSON, exit." There's no long-lived editor session to amortize a resident daemon against — warming and tearing down a daemon for a single report is pure overhead.

  3. Reproducible benchmark of two configurations. Same binary, flip one variable, diff the output. A resident daemon adds shared cache/snapshot state between runs that I specifically wanted out of the experiment.

So the value I'm actually after is "run an analyzer over a path without standing up a daemon." If you'd rather express that some other way — e.g. the existing daemon path auto-spawning an ephemeral in-process instance when no daemon is reachable, instead of a distinct daemonless concept — I'm very happy to rework #84 to fit whatever you land on. I don't want to lock in a parallel mode that fights your direction.

What does your alternative resolution look like so far? If you can point me at the shape you're leaning toward, I can adapt this PR (or close it and fold the CLI surface into your approach). No rush.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants