Skip to content
Merged

V1.0 #233

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
73e88f7
feat: Migrate deps config (#230)
hound672 Aug 26, 2026
38ec00d
task: Add replace directive (#231)
hound672 Sep 20, 2026
f17ed2c
feat(v1): add pilot module and generation flow
Yakwilik Sep 23, 2026
4ff336f
Refactor optional Git dependency config loading
Yakwilik Sep 23, 2026
bb9cfa1
refactor(v1): remove legacy command paths and schema surfaces
Yakwilik Sep 23, 2026
4143ecf
chore(v1): tidy dependencies after legacy removal
Yakwilik Sep 23, 2026
5fd99b6
Complete v1 validation and breaking policy routing
Yakwilik Sep 23, 2026
1226865
Add v1 vendor, source listing, and schema generation
Yakwilik Sep 23, 2026
d671e60
Select Git dependency config modes before parsing
Yakwilik Sep 23, 2026
7031566
Support nested Git modules by tag or commit
Yakwilik Sep 23, 2026
02e0d5a
Resolve untagged nested modules from Git HEAD
Yakwilik Sep 23, 2026
01dfcc6
feat(v1): add get command for Git modules
Yakwilik Sep 23, 2026
9f75834
Restore schema-based YAML validation for v1 configs
Yakwilik Sep 23, 2026
0f381cd
Validate all nested v1 config files by default
Yakwilik Sep 23, 2026
f67227b
Refactor v1 command flows and configuration boundaries
Yakwilik Sep 23, 2026
7e8fd13
Simplify v1 execution paths and strengthen table-driven tests
Yakwilik Sep 23, 2026
088847e
Fix manifest block editing and validation output errors
Yakwilik Sep 23, 2026
5a68666
Refactor v1 module and generation boundaries
Yakwilik Sep 24, 2026
8a3d55c
Complete v1 configuration validation and restore MCP tool
Yakwilik Sep 24, 2026
1791ecc
Restore explicit plugin paths and commands in v1 generation
Yakwilik Sep 24, 2026
f448dc3
fix v1 dependency and descriptor regressions
Yakwilik Sep 24, 2026
0b2e69e
fix v1 derived requirements and descriptor selection
Yakwilik Sep 24, 2026
c916af8
feat: combine descriptors from multiple v1 modules
Yakwilik Sep 24, 2026
c01e130
fix v1 descriptor sets and derived requirements
Yakwilik Sep 24, 2026
f973d1a
Merge main into feature/v1.0-pilot
Yakwilik Sep 29, 2026
27885da
feat(v1): export independent descriptor sets after preflight
Yakwilik Sep 29, 2026
d4cc1a3
fix(v1): address lint, replacement, baseline and legacy dependency re…
Yakwilik Sep 29, 2026
3352ec0
fix(v1): isolate breaking baselines and managed Go options
Yakwilik Sep 29, 2026
bfc3806
feat(v1): finalize explicit generation and dependency contracts
Yakwilik Sep 30, 2026
6c2d5db
feat(v1): add verified migration and finish policy correctness
Yakwilik Sep 30, 2026
d8a7c65
feat(migrate): add a safe interactive terminal wizard
Yakwilik Sep 30, 2026
103af8f
fix(v1): close remaining import and validation review gaps
Yakwilik Sep 30, 2026
4bce3c0
fix: repair developer tooling and pinned dependency diagnostics
Yakwilik Sep 30, 2026
f938a3b
feat(mod): isolate local replacement graphs from published locks
Yakwilik Sep 30, 2026
bf4c63d
feat: enforce explicit frozen dependency graphs
Yakwilik Sep 30, 2026
4d53e10
feat(mod): align major versions with Go semantics
Yakwilik Sep 30, 2026
341ca3a
fix(mod): surface unsupported Buf registry dependencies
Yakwilik Sep 30, 2026
70ef18e
feat(policy): resolve section-scoped bases from verified modules
Yakwilik Sep 30, 2026
2d0102c
feat(generate): select exact protobuf packages before compilation
Yakwilik Sep 30, 2026
4e3540d
feat(breaking): implement descriptor-based compatibility profiles
Yakwilik Sep 30, 2026
6e16cde
feat(mcp): describe module grammar and locked dependencies
Yakwilik Sep 30, 2026
81b2f64
Merge pull request #232 from easyp-tech/feature/v1.0-pilot
Yakwilik Oct 3, 2026
2777ded
feat(mod): resolve BSR dependencies and harden Git compatibility
Yakwilik Oct 3, 2026
e173b4b
fix(cache): serialize concurrent module installs
Yakwilik Oct 3, 2026
fef3434
fix(policy): isolate dependency policies from environment
Yakwilik Oct 3, 2026
c5328a5
fix(migrate): accept v0 compatibility metadata
Yakwilik Oct 3, 2026
d575887
perf(cache): avoid rehashing unchanged snapshots
Yakwilik Oct 3, 2026
d2731d8
fix(generate): explain empty automatic selection
Yakwilik Oct 3, 2026
6636d7a
fix(mod): reject embedded manifest versions
Yakwilik Oct 3, 2026
6359d5c
test(policy): cover breaking inheritance resolver
Yakwilik Oct 3, 2026
bf9eeab
fix(mod): reject normalized root escapes
Yakwilik Oct 3, 2026
db90480
fix(schema): constrain plugin versions by source
Yakwilik Oct 3, 2026
2f187ec
fix(mod): include source line for missing module identity
Yakwilik Oct 3, 2026
24b6618
fix(frozen): explain missing lock recovery
Yakwilik Oct 3, 2026
87802b8
fix(files): use durable normal project-file writes
Yakwilik Oct 3, 2026
84187ac
fix(ls-files): guide legacy projects to migration
Yakwilik Oct 3, 2026
f78dadf
fix(cli): avoid terminal appearance probes
Yakwilik Oct 3, 2026
38a8f5c
fix(generate): explain path-like module selections
Yakwilik Oct 3, 2026
c4a3244
fix(breaking): default v1 policies to FILE profile
Yakwilik Oct 4, 2026
4fdb82c
fix(policy): avoid checking replacement modules twice
Yakwilik Oct 4, 2026
28d6e84
fix(git): isolate local module repository discovery
Yakwilik Oct 4, 2026
efeceb2
fix(lint): report package paths relative to modules
Yakwilik Oct 4, 2026
8858218
fix(generate): separate Go outputs by protobuf package
Yakwilik Oct 4, 2026
779171c
ci: validate v1 pushes and Go lint
Yakwilik Oct 4, 2026
1afb5a9
fix(cache): compile verification identity on Linux ARM
Yakwilik Oct 4, 2026
f4b45a0
release: package MCP and pin release tooling
Yakwilik Oct 4, 2026
733b8b9
fix(cli): make help and quick start actionable
Yakwilik Oct 4, 2026
b6c7344
fix(ci): satisfy enabled Go lint checks
Yakwilik Oct 4, 2026
5c10132
test(breaking): align default FILE profile regression
Yakwilik Oct 4, 2026
75fa144
refactor: remove unreachable internal code
Yakwilik Oct 4, 2026
fb56b15
docs(v1): record superseded pilot RFC contract
Yakwilik Oct 4, 2026
9bb6409
fix(lint): accept module-qualified package layouts
Yakwilik Oct 4, 2026
d70401e
fix(validate): preserve remote version correction
Yakwilik Oct 4, 2026
ece51fe
fix(v1): align Go output and dependency contexts
Yakwilik Oct 4, 2026
6e04298
fix(v1): scope replacement sources across snapshots
Yakwilik Oct 4, 2026
20e3f7a
fix(v1): resolve generation and dependency review regressions
Yakwilik Oct 5, 2026
1b04a83
build(release): keep v1 nightly isolated from stable installs
Yakwilik Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
112 changes: 112 additions & 0 deletions .agents/skills/epctl-commands/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
name: epctl-commands
description: "Use when adding or changing EasyP CLI commands, handlers in internal/api, or registration in cmd/easyp/main.go. epctl-commands is the retained legacy skill name."
argument-hint: "Describe the EasyP CLI command you want to add or change"
---

# EasyP CLI Commands — Legacy Skill Name: epctl-commands

The name <code>epctl-commands</code> and its installation path are retained for compatibility. This skill routes current CLI work to <code>github.com/easyp-tech/easyp</code>. EasyP uses <code>github.com/urfave/cli/v2</code>, with handlers in <code>internal/api</code> and registration in <code>cmd/easyp/main.go</code>.

Start with [AGENTS.md](../../../AGENTS.md), the [agent rules](../../../.spec/agent-rules.md), and [.spec/CLI.md](../../../.spec/CLI.md). Read the nearest handler and its tests before extending a command.

## Source Map

| Responsibility | Actual source |
|----------------|---------------|
| Root <code>cli.App</code>, logger initialization, registration | [cmd/easyp/main.go](../../../cmd/easyp/main.go) |
| <code>Handler</code> contract: <code>Command() *cli.Command</code> | [internal/api/interface.go](../../../internal/api/interface.go) |
| Small handler example | [internal/api/schema_gen.go](../../../internal/api/schema_gen.go) |
| Group and subcommands | [internal/api/mod.go](../../../internal/api/mod.go) |
| Validation and text/JSON reports | [internal/api/validate.go](../../../internal/api/validate.go) |
| Logger lookup and core construction | [internal/api/runtime.go](../../../internal/api/runtime.go) |
| Root flags and output format selection | [internal/flags/flags.go](../../../internal/flags/flags.go), [format.go](../../../internal/flags/format.go) |

Use neighboring file names such as <code>schema_gen.go</code>, <code>mod.go</code>, and <code>mod_v1_update.go</code>. Place CLI wiring in <code>internal/api</code>; keep operations in their existing packages: <code>internal/modules</code> for dependencies, <code>internal/generation</code> for generation orchestration, <code>internal/migration</code> for migration, and <code>internal/core</code> for engines.

## urfave/cli v2 Pattern

- The root is a <code>*cli.App</code> with a <code>Commands</code> slice.
- Handlers implement <code>Command() *cli.Command</code>, often on a small exported struct.
- Actions have signature <code>func(ctx *cli.Context) error</code>. Read flags through <code>ctx.String</code>, <code>ctx.Bool</code>, and related methods; use <code>ctx.Args()</code> for positional arguments.
- Pass <code>ctx.Context</code> to operations requiring <code>context.Context</code>.
- Groups put children in <code>cli.Command.Subcommands</code>. The root app's <code>Commands</code> and a group's <code>Subcommands</code> are different fields.

This complete handler-file example adapts the existing <code>SchemaGen</code> handler and uses the real [schemagen API](../../../internal/schemagen/schemagen.go). It shows fresh command-local flags and separate assignment/error checking. It illustrates a replacement pattern for that file, not a second handler to add beside the existing one.

~~~go
package api

import (
"fmt"

"github.com/urfave/cli/v2"

"github.com/easyp-tech/easyp/internal/schemagen"
)

// SchemaGen writes JSON Schemas for the v1 YAML documents.
type SchemaGen struct{}

var _ Handler = (*SchemaGen)(nil)

// Command implements Handler.
func (s SchemaGen) Command() *cli.Command {
return &cli.Command{
Name: "schema-gen",
Usage: "generate JSON Schemas for v1 YAML files",
Action: s.Action,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "out-dir",
Usage: "directory for generated v1 JSON Schemas",
Value: schemagen.DefaultOutDir,
},
},
}
}

// Action writes the schemas to the selected output directory.
func (s SchemaGen) Action(ctx *cli.Context) error {
err := schemagen.Run(schemagen.Options{OutDir: ctx.String("out-dir")})
if err != nil {
return fmt.Errorf("Run: %w", err)
}
return nil
}
~~~

For engine work, follow <code>buildCore</code> and [core.New(core.Options)](../../../internal/core/core.go). Consult the actual engine signature, such as <code>Core.Lint(context.Context, DirWalker) ([]IssueInfo, error)</code> in [core/lint.go](../../../internal/core/lint.go), rather than inventing a service client or server lifecycle.

## Wiring a Command

1. Add or update the handler in <code>internal/api</code>, implementing <code>Handler</code>. Keep parsing, output, and CLI error decisions at this boundary.
2. For a top-level command, add the handler value to the existing <code>buildCommand(...)</code> call in <code>main</code>. For example, <code>api.SchemaGen{}</code> is already registered there. The helper calls each handler's <code>Command()</code>.
3. For a subcommand, extend the parent's <code>Subcommands</code> slice. Follow <code>Mod.Command</code>, which binds actions such as <code>m.Download</code> and <code>m.Update</code>.
4. Give flags accurate names, aliases, usage text, defaults, and required behavior. Preserve public spellings and inspect parent/local flag precedence.
5. Add focused action tests and registration/argument tests where needed. Use [go-testing](../go-testing/SKILL.md) for process-state and urfave flag isolation.

## Output, Logging, and Errors

- The root defines <code>--cfg</code> (alias <code>--config</code>), <code>--debug</code>, and <code>--format</code> (alias <code>-f</code>). Text/JSON support and default format are command-specific; use <code>flags.GetFormat</code> where appropriate.
- Follow the target command's existing output contract. For writer-based output, use <code>ctx.App.Writer</code> and <code>ctx.App.ErrWriter</code>, with appropriate standard-stream fallbacks when actions can be invoked directly. <code>Validate.Action</code> demonstrates reports through the application writer.
- Preserve output write failures with <code>%w</code>, including buffered flush/JSON encode failures. EasyP has no universal printer abstraction or global <code>--output</code> mode.
- Use <code>getLogger(ctx)</code> for the repository logger. The root installs it in application metadata; do not introduce unrelated global logger state.
- Wrap call failures using only the callee name, such as <code>fmt.Errorf("Run: %w", err)</code>, without a receiver/package prefix. Follow [go-code-style](../go-code-style/SKILL.md).
- Inspect the handler, <code>runtime.go</code>, and <code>main.go</code> before changing exits. Some handlers return errors, some use <code>cli.Exit</code>, and some call process-exit helpers. [.spec/ERRORS.md](../../../.spec/ERRORS.md) documents these boundaries; there is no general gRPC status mapper.

## CLI Test Isolation

Construct fresh commands, flags, mutable flag values, contexts, and writers per parallel case. urfave/cli v2 shares <code>HelpFlag</code>: use <code>HideHelp: true</code> on the test app and every command/subcommand when help is irrelevant. <code>HideHelpCommand</code> alone is insufficient; use <code>HideVersion: true</code> on the app for the shared version flag too.

Action-only tests can use a private <code>flag.FlagSet</code> with <code>cli.NewContext</code>. Tests that alter cwd, environment, or global context/CLI hooks stay sequential or run in subprocesses; paths that exit the process need subprocess coverage. See [go-testing](../go-testing/SKILL.md) and [breaking_baseline_test.go](../../../internal/api/breaking_baseline_test.go).

## Quick Checklist

- [ ] Work targets the EasyP CLI; the legacy skill name/path remain intact.
- [ ] Handler and action signatures use urfave/cli v2.
- [ ] Registration uses <code>buildCommand</code> for root commands and <code>Subcommands</code> for groups.
- [ ] Operations use actual package APIs and preserve context cancellation.
- [ ] Flags, output formats, logger use, and exit behavior match the command contract.
- [ ] Errors retain causes and use callee-only labels.
- [ ] Tests isolate mutable CLI flags and respect process-state constraints.
108 changes: 108 additions & 0 deletions .agents/skills/go-code-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
name: go-code-style
description: "Use when writing, reviewing, or refactoring Go code or creating packages in the github.com/easyp-tech/easyp CLI repository."
argument-hint: "Describe the Go code you are writing or reviewing"
---

# Go Code Style — EasyP CLI

Apply these conventions to <code>github.com/easyp-tech/easyp</code>, the Protocol Buffers CLI toolkit. Start with [AGENTS.md](../../../AGENTS.md), [.spec/README.md](../../../.spec/README.md), and the mandatory [agent rules](../../../.spec/agent-rules.md). Verify relevant source before changing behavior; older code may not follow every current convention.

## Errors and Resource Cleanup

- Wrap propagated call failures with <code>fmt.Errorf("&lt;callee&gt;: %w", err)</code>. Use only the called function or method name, without a package or receiver prefix.
- For <code>os.Open</code>, use <code>"Open: %w"</code>; for <code>source.Fetch</code>, use <code>"Fetch: %w"</code>; for <code>c.protoInfoRead</code>, use <code>"protoInfoRead: %w"</code>.
- Use <code>errors.Is</code> for sentinel identity and <code>errors.As</code> for typed error details. Reuse errors in their owning packages rather than inventing duplicates.
- Never use a bare <code>defer resource.Close()</code> or discard the close error. Wrap the deferred call and log or handle its error. Propagate finalization failures when they affect successful output.
- Log a failure where it is handled or terminates an operation; lower layers normally wrap and return it.

Error ownership follows actual source:

| Owner | Examples |
|-------|----------|
| [internal/core/core.go](../../../internal/core/core.go) | <code>ErrInvalidRule</code>, <code>ErrRepositoryDoesNotExist</code>, <code>ErrEmptyInputFiles</code> |
| [internal/core/dom.go](../../../internal/core/dom.go) | <code>OpenImportFileError</code>, <code>GitRefNotFoundError</code> |
| [internal/modules/immutable_versions.go](../../../internal/modules/immutable_versions.go) | <code>ErrLockedVersionChanged</code> |
| [internal/config/v1/legacy_detection.go](../../../internal/config/v1/legacy_detection.go) | <code>ErrLegacyConfiguration</code> |
| [internal/migration](../../../internal/migration) | Contextual planning and apply errors |

See [.spec/ERRORS.md](../../../.spec/ERRORS.md) and the relevant CLI handler for reporting and exit behavior. There is no central domain-to-gRPC-status mapper. Follow [cmd/easyp/main.go](../../../cmd/easyp/main.go) and the handler's actual return/exit path instead of assigning a universal exit code to a sentinel.

This complete, generic standalone example illustrates wrapping and cleanup; it is not an existing EasyP helper:

~~~go
package example

import (
"fmt"
"io"
"log/slog"
"os"
)

// ReadText reads a file and reports any close failure to the logger.
func ReadText(path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("Open: %w", err)
}
defer func() {
err := file.Close()
if err != nil {
slog.Error("Close", "error", err)
}
}()

data, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("ReadAll: %w", err)
}
return string(data), nil
}
~~~

## Assignments, Imports, and Comments

- Assign existing variables and check errors on separate lines. A block-scoped short declaration with <code>:=</code> in an <code>if</code> initializer is allowed.
- Put comments above control flow, never inline on <code>if</code>, <code>for</code>, or <code>return</code> lines.
- Group imports as standard library, third-party, then project packages, separated by blank lines. Project imports start with <code>github.com/easyp-tech/easyp</code>.
- Use English comments and godoc. Every exported symbol needs a comment starting with its name. Put a package comment in one file per package.
- Format Go code with <code>gofmt</code>. Import grouping and tag spellings are repository conventions: [.golangci.yml](../../../.golangci.yml) explicitly enables <code>staticcheck</code>, not <code>gci</code> or <code>tagliatelle</code>. Do not infer enabled linters from these conventions.

## Naming and Package Boundaries

| Responsibility | Source to follow |
|----------------|------------------|
| CLI handlers | [internal/api](../../../internal/api), implementing <code>Handler.Command() *cli.Command</code> |
| Process entry and command registration | [cmd/easyp/main.go](../../../cmd/easyp/main.go) |
| Lint/breaking engines and low-level plugin execution | [internal/core](../../../internal/core) |
| Dependency resolution and repositories | [internal/modules](../../../internal/modules) |
| Generation orchestration | [internal/generation](../../../internal/generation) |
| v1 configuration and schema source | [internal/config/v1](../../../internal/config/v1) |
| Lint rules and tests | [internal/rules](../../../internal/rules), colocated <code>&lt;rule&gt;.go</code> and <code>&lt;rule&gt;_test.go</code> |

Use exported domain types and adapter implementations where required by their consumers; keep local configuration structs unexported. Existing public models such as <code>core.Options</code> and <code>v1.Policy</code> stay exported. Follow neighboring filenames and colocate tests as <code>&lt;file&gt;_test.go</code>.

Reserve zero for new enum types with <code>_ = iota</code> so an unset value is not silently valid. Preserve established public configuration spellings and formats when extending existing types.

## Interfaces and Context

- Put <code>context.Context</code> first in methods that need it, and <code>error</code> last in results. Preserve existing contracts that do not take a context.
- Define small interfaces in the consuming package; prefer one method when sufficient. Do not prefix interface names with <code>I</code>.
- Actual contracts include <code>core.Rule</code> and <code>core.CurrentProjectGitWalker</code> in [dom.go](../../../internal/core/dom.go), <code>modules.Source</code> in [resolve.go](../../../internal/modules/resolve.go), and repository/cache interfaces in [repository.go](../../../internal/modules/repository.go).
- <code>Console</code> belongs to [internal/adapters/console/new.go](../../../internal/adapters/console/new.go). Use the current owner when implementing or mocking it.
- CLI actions receive <code>*cli.Context</code> from urfave/cli v2; pass <code>ctx.Context</code> to context-aware operations. Keep cancellation and mutable state scoped to the operation. Choose concurrency from the actual engine/adapter contract.

## Struct Tags

Follow the existing model's tags. v1 configuration types live in <code>internal/config/v1</code>, shared engine configuration in <code>internal/config</code>. Use the established YAML/JSON keys, usually <code>snake_case</code>, and retain hyphenated public keys such as <code>linters-settings</code> and <code>exclude-rules</code>. Avoid adding serialization tags to internal-only types without a consumer. Never hand-edit generated protobuf code or schema JSON.

## Quick Checklist

- [ ] Error wraps contain the callee name only, with <code>%w</code> and no package/receiver prefix.
- [ ] Existing-variable assignment and error checking are separate; deferred close errors are handled.
- [ ] Imports use the repository module path and the three conventional groups.
- [ ] Exported symbols have English godoc; control-flow comments are on separate lines.
- [ ] Errors and interfaces stay with their actual owners; new enums reserve zero.
- [ ] Public tag spellings are preserved; lint claims match the current configuration.
- [ ] Tests follow [go-testing](../go-testing/SKILL.md); CLI changes follow the legacy-named [epctl-commands](../epctl-commands/SKILL.md).
Loading
Loading