add Go sidecar and CLI skeleton
cmd/clide/main.go is the single entry; the same binary will later switch into sidecar-daemon mode via --daemon. internal/cli holds the dispatch (stdlib flag for now, Cobra arrives with the first real subcommand in Tier 2). internal/diag mirrors pql's exit-code contract so the two tools share one mental model for callers. internal/version exposes the build-info struct that the Makefile's -ldflags -X targets stamp; Info() is wired to --version so the binary reports both the declared project.yaml version and the commit it was built from. The daemon, pty, proc, git, ipc, and pql sub-packages ship as doc.go stubs naming the subsystem they will own. They keep the layout legible before the code lands so tier work has a home on arrival. Module path targets Gitea (git.schweitz.net/jpmschweitzer/clide/sidecar), matching where the repo lives. Go module resolution works if the host serves the meta tags; a vanity import path can be added later if needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit.
|
|||||||
[ADR 0002](docs/ADRs/0002-sidecar-language-go.md) — Sidecar language: Go.
|
[ADR 0002](docs/ADRs/0002-sidecar-language-go.md) — Sidecar language: Go.
|
||||||
[ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md) — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present.
|
[ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md) — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present.
|
||||||
[ADR 0004](docs/ADRs/0004-ignore-file-strategy.md) — Ignore file strategy (`ignore_files:` in `.pql/config.yaml`, layered).
|
[ADR 0004](docs/ADRs/0004-ignore-file-strategy.md) — Ignore file strategy (`ignore_files:` in `.pql/config.yaml`, layered).
|
||||||
|
- Go sidecar/CLI skeleton under `sidecar/` (module `git.schweitz.net/jpmschweitzer/clide/sidecar`): `cmd/clide/main.go`, `internal/cli` with a stdlib-flag dispatch, `internal/diag` mirroring pql's exit-code + stderr-JSON contract, `internal/version` with ldflag-stamped build info, and placeholder packages for `daemon`, `pty`, `proc`, `git`, `ipc`, `pql` awaiting their tier. `clide --version` emits JSON build-info today.
|
||||||
- Root `Makefile` drives both the Go sidecar and the Flutter app under one toolchain. Version is read from `project.yaml` via awk and stamped into the sidecar via `-ldflags -X`. Flutter targets gracefully noop before the app is scaffolded so the Makefile is usable from day one. Pinned Go tooling (govulncheck, goimports, golangci-lint) installs via `make tools`.
|
- Root `Makefile` drives both the Go sidecar and the Flutter app under one toolchain. Version is read from `project.yaml` via awk and stamped into the sidecar via `-ldflags -X`. Flutter targets gracefully noop before the app is scaffolded so the Makefile is usable from day one. Pinned Go tooling (govulncheck, goimports, golangci-lint) installs via `make tools`.
|
||||||
- `ci/` entry scripts: `test.sh`, `lint.sh` (includes the supply-chain gate — no green lint without a green CVE scan), `security.sh`, `release.sh` (stub).
|
- `ci/` entry scripts: `test.sh`, `lint.sh` (includes the supply-chain gate — no green lint without a green CVE scan), `security.sh`, `release.sh` (stub).
|
||||||
- Project identity files for the Flutter rebuild at the repo root: `project.yaml` (single source of truth for version + module path, version 2.0.0-dev), a fresh `README.md`, MIT `LICENSE`, and `.editorconfig`. The Python clide's manifest and README are preserved under `legacy/`.
|
- Project identity files for the Flutter rebuild at the repo root: `project.yaml` (single source of truth for version + module path, version 2.0.0-dev), a fresh `README.md`, MIT `LICENSE`, and `.editorconfig`. The Python clide's manifest and README are preserved under `legacy/`.
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Command clide is the CLI and sidecar-daemon entry point.
|
||||||
|
//
|
||||||
|
// One binary, two modes:
|
||||||
|
// - One-shot CLI (default): clide <subcommand> [args...]
|
||||||
|
// - Long-running sidecar: clide --daemon
|
||||||
|
//
|
||||||
|
// See docs/initial-plan.md and docs/ADRs/ for the architecture.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.schweitz.net/jpmschweitzer/clide/sidecar/internal/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
os.Exit(cli.Run(os.Args[1:]))
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module git.schweitz.net/jpmschweitzer/clide/sidecar
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Package cli is the CLI front-end. cmd/clide/main.go calls Run with
|
||||||
|
// os.Args[1:]. Subcommands land here one file per command once Tier 2
|
||||||
|
// begins (see docs/initial-plan.md).
|
||||||
|
//
|
||||||
|
// Stdlib-only for the pre-Tier-0 scaffold. Cobra arrives with the first
|
||||||
|
// real subcommand surface.
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.schweitz.net/jpmschweitzer/clide/sidecar/internal/diag"
|
||||||
|
"git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run dispatches CLI args. Returns the process exit code per the diag
|
||||||
|
// contract.
|
||||||
|
func Run(args []string) int {
|
||||||
|
fs := flag.NewFlagSet("clide", flag.ContinueOnError)
|
||||||
|
fs.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
daemon := fs.Bool("daemon", false, "run as the long-running sidecar daemon")
|
||||||
|
showVersion := fs.Bool("version", false, "print build info and exit")
|
||||||
|
|
||||||
|
if err := fs.Parse(args); err != nil {
|
||||||
|
return diag.Usage
|
||||||
|
}
|
||||||
|
|
||||||
|
if *showVersion {
|
||||||
|
b, _ := json.Marshal(version.Info())
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return diag.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
if *daemon {
|
||||||
|
diag.Error("cli.not-implemented", "daemon mode not yet implemented", "tier 0 — sidecar scaffold lands next")
|
||||||
|
return diag.Software
|
||||||
|
}
|
||||||
|
|
||||||
|
// No subcommand yet. Surface the pql-style exit 64 so callers can tell
|
||||||
|
// the difference between "no instructions" and a successful run.
|
||||||
|
rest := fs.Args()
|
||||||
|
if len(rest) == 0 {
|
||||||
|
diag.Error("cli.usage", "no subcommand given", "see docs/initial-plan.md — Tier 2 defines the CLI surface")
|
||||||
|
return diag.Usage
|
||||||
|
}
|
||||||
|
|
||||||
|
diag.Error("cli.not-implemented", fmt.Sprintf("subcommand %q not yet implemented", rest[0]), "tier 2 lands the CLI surface")
|
||||||
|
return diag.Software
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Package daemon hosts the --daemon-mode entry point: lifecycle
|
||||||
|
// management (socket binding, token-auth setup, single-instance lock,
|
||||||
|
// graceful shutdown), subsystem wiring (pty, proc, git, pql, ipc), and
|
||||||
|
// the long-lived event loop.
|
||||||
|
//
|
||||||
|
// Empty for now — lands in Tier 0.
|
||||||
|
package daemon
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Package diag defines the exit-code contract and emits structured
|
||||||
|
// diagnostics to stderr as line-delimited JSON.
|
||||||
|
//
|
||||||
|
// Mirrors pql's diag contract so Claude (and any other tool that
|
||||||
|
// consumes both CLIs) sees a single shape.
|
||||||
|
package diag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exit codes. Same numeric space as pql's.
|
||||||
|
const (
|
||||||
|
OK = 0 // success
|
||||||
|
NoMatch = 2 // success with zero matches / nothing to do
|
||||||
|
Usage = 64 // EX_USAGE — bad CLI flag or missing subcommand
|
||||||
|
DataErr = 65 // EX_DATAERR — malformed request
|
||||||
|
NoInput = 66 // EX_NOINPUT — missing required resource (repo, socket, file)
|
||||||
|
Unavail = 69 // EX_UNAVAILABLE — sidecar unreachable / subsystem down
|
||||||
|
Software = 70 // EX_SOFTWARE — internal error
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelWarn Level = "warn"
|
||||||
|
LevelError Level = "error"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Diagnostic is one entry in the stderr JSON-per-line stream.
|
||||||
|
type Diagnostic struct {
|
||||||
|
Level Level `json:"level"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Hint string `json:"hint,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit writes a diagnostic as one JSON line to w.
|
||||||
|
func Emit(w io.Writer, d Diagnostic) {
|
||||||
|
b, err := json.Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
_, _ = io.WriteString(w, `{"level":"error","code":"diag.marshal","msg":"failed to marshal diagnostic"}`+"\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = w.Write(append(b, '\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn emits a warning diagnostic to stderr.
|
||||||
|
func Warn(code, msg string) {
|
||||||
|
Emit(os.Stderr, Diagnostic{Level: LevelWarn, Code: code, Msg: msg})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error emits an error diagnostic to stderr.
|
||||||
|
func Error(code, msg, hint string) {
|
||||||
|
Emit(os.Stderr, Diagnostic{Level: LevelError, Code: code, Msg: msg, Hint: hint})
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Package git shells out to git for the Git panel, diff rendering, and
|
||||||
|
// the `clide git <subcommand>` CLI surface.
|
||||||
|
//
|
||||||
|
// Empty for now — lands in Tier 3.
|
||||||
|
package git
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Package ipc hosts the unix-socket JSON-lines server the sidecar
|
||||||
|
// exposes to the Flutter app and CLI. Token-authenticated; dispatches
|
||||||
|
// to the pty, proc, git, pql, canvas, and pane subsystems.
|
||||||
|
//
|
||||||
|
// Empty for now — lands in Tier 0.
|
||||||
|
package ipc
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Package pql is the *only* place Clide contains pql logic. Pure
|
||||||
|
// shell-outs to the `pql` binary — no re-implementation of pql's
|
||||||
|
// indexer, ranker, or frontmatter parsing.
|
||||||
|
//
|
||||||
|
// See docs/ADRs/0003-pql-as-supporter-tool.md for the wrap-don't-
|
||||||
|
// duplicate rule and the "pql is a Clide subsystem when present"
|
||||||
|
// invariant.
|
||||||
|
//
|
||||||
|
// Empty for now — lands in Tier 4.
|
||||||
|
package pql
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Package proc manages subprocess lifecycles spawned by the sidecar —
|
||||||
|
// git operations, pql invocations, and anything else the app or CLI
|
||||||
|
// requests.
|
||||||
|
//
|
||||||
|
// Empty for now — lands alongside Tier 1/3 subsystems.
|
||||||
|
package proc
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Package pty owns the PTY lifecycle for terminals hosted inside the
|
||||||
|
// sidecar daemon. Drives xterm.dart panes in the Flutter app and the
|
||||||
|
// `clide` CLI's direct-attach mode.
|
||||||
|
//
|
||||||
|
// Empty for now — lands in Tier 1.
|
||||||
|
package pty
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Package version exposes build-info stamped via -ldflags at build time.
|
||||||
|
// See the root Makefile's LDFLAGS for the -X targets.
|
||||||
|
package version
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version = "dev"
|
||||||
|
Commit = "unknown"
|
||||||
|
Date = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchemaVersion tracks the app↔sidecar IPC contract. Bump when the wire
|
||||||
|
// format changes in a way that requires coordinated app and sidecar
|
||||||
|
// updates. Mirror in project.yaml `schema_version:` when that field is
|
||||||
|
// added there.
|
||||||
|
const SchemaVersion = 0
|
||||||
|
|
||||||
|
type BuildInfo struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Commit string `json:"commit"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
GoVersion string `json:"go_version"`
|
||||||
|
SchemaVersion int `json:"schema_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info() BuildInfo {
|
||||||
|
return BuildInfo{
|
||||||
|
Version: Version,
|
||||||
|
Commit: Commit,
|
||||||
|
Date: Date,
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
SchemaVersion: SchemaVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user