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 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).
|
||||
- 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`.
|
||||
- `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/`.
|
||||
|
||||
@@ -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