Add aliases to maintained examples
This commit is contained in:
@@ -25,7 +25,7 @@ integration.
|
||||
|
||||
## Examples
|
||||
|
||||
- [Core-only example](examples/core/main.go)
|
||||
- [Core-only example](examples/core/README.md)
|
||||
- [ConfigKit example](integrations/ckconfig/examples/simple/README.md)
|
||||
- [Composed ConfigKit and PromptKit example](integrations/ckprompt/examples/composed/README.md)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Application-owned aliases implementation plan
|
||||
|
||||
Status: in progress; Stages 1–13 completed.
|
||||
Status: in progress; Stages 1–14 completed.
|
||||
|
||||
## Authority, baseline, and scope
|
||||
|
||||
@@ -657,6 +657,17 @@ the examples remain complete, copyable artifacts. Explain local source testing
|
||||
and forthcoming core dependency adoption in their developer-facing guidance;
|
||||
do not claim published core v0.2.0 has alias support.
|
||||
|
||||
**Completed:** The core example now installs command and group shortcuts,
|
||||
global and scoped flag aliases in a mixed declaration slice, and prints entered
|
||||
and canonical invocation paths. The composed example installs collection,
|
||||
PromptKit group, bundle, smoke, scoped format, and global help aliases alongside
|
||||
its complete canonical declarations. Example tests cover shortcut and
|
||||
canonical operations, help with zero PromptKit engine loads, and local smoke
|
||||
report delivery. Both example READMEs explain local-source use and that
|
||||
published core v0.2.0 lacks alias support. Verified with `go test ./... -count=1`
|
||||
at the root and the complete ckprompt module suite under the local workspace;
|
||||
all newly documented alias commands also ran successfully.
|
||||
|
||||
## Stage 15: Reconcile public and internal documentation
|
||||
|
||||
**Work**
|
||||
|
||||
22
examples/core/README.md
Normal file
22
examples/core/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Core example
|
||||
|
||||
Run the example from the repository root:
|
||||
|
||||
```sh
|
||||
go run ./examples/core hello --times=0 --tag=first
|
||||
go run ./examples/core ws greet --count=2
|
||||
go run ./examples/core ws --help
|
||||
go run ./examples/core hello --hlep
|
||||
```
|
||||
|
||||
The example installs a canonical `workspace greet` command together with a
|
||||
command shortcut, a group shortcut, a scoped flag alias, and a global help
|
||||
alias. Its declaration slice mixes the canonical group and routing rules as
|
||||
`[]commandkit.Declaration`. The handler prints `Invocation.EnteredPath()` and
|
||||
`Invocation.CanonicalPath()` so the alternate route and its canonical target
|
||||
are visible in the output.
|
||||
|
||||
These aliases use the current local CommandKit source. Published CommandKit
|
||||
v0.2.0 does not include alias declarations; consumers can use them after the
|
||||
next core release. Repository contributors can run its test with
|
||||
`go test ./examples/core` from the root.
|
||||
@@ -37,14 +37,21 @@ func newApp() (*commandkit.App, error) {
|
||||
if input.Tags.Set {
|
||||
tags = fmt.Sprintf("explicit %v", input.Tags.Value)
|
||||
}
|
||||
if _, err := fmt.Fprintf(invocation.Streams.Out, "Hello; count=%s; tags=%s\n", count, tags); err != nil {
|
||||
if _, err := fmt.Fprintf(invocation.Streams.Out, "Hello; count=%s; tags=%s; entered=%q; canonical=%q\n", count, tags, invocation.EnteredPath(), invocation.CanonicalPath()); err != nil {
|
||||
return commandkit.Failure(err)
|
||||
}
|
||||
return commandkit.Success()
|
||||
},
|
||||
})
|
||||
workspace := commandkit.Group("workspace", "Workspace commands.", greet)
|
||||
return commandkit.New(commandkit.Identity{Name: "core-example", Version: "1.0"}, workspace)
|
||||
declarations := []commandkit.Declaration{
|
||||
workspace,
|
||||
commandkit.Alias("hello", "workspace greet"),
|
||||
commandkit.Alias("ws", "workspace"),
|
||||
commandkit.FlagAliasFor("workspace greet", "--times", "--count"),
|
||||
commandkit.FlagAlias("--hlep", "--help"),
|
||||
}
|
||||
return commandkit.New(commandkit.Identity{Name: "core-example", Version: "1.0"}, declarations...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
63
examples/core/main_test.go
Normal file
63
examples/core/main_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
)
|
||||
|
||||
func TestAliasesReachCoreExampleCommand(t *testing.T) {
|
||||
app, err := newApp()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
args []string
|
||||
entered string
|
||||
canonical string
|
||||
greeting string
|
||||
}{
|
||||
{name: "command shortcut and scoped flag", args: []string{"hello", "--times=0", "--tag", "one"}, entered: "hello", canonical: "workspace greet", greeting: "count=explicit 0; tags=explicit [one]"},
|
||||
{name: "group shortcut", args: []string{"ws", "greet", "--count=2"}, entered: "ws greet", canonical: "workspace greet", greeting: "count=explicit 2"},
|
||||
{name: "canonical command", args: []string{"workspace", "greet"}, entered: "workspace greet", canonical: "workspace greet", greeting: "count=default 1 (not supplied)"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), test.args, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess {
|
||||
t.Fatalf("Execute(%q) = %#v; output %q", test.args, outcome, output.String())
|
||||
}
|
||||
for _, fragment := range []string{
|
||||
test.greeting,
|
||||
"entered=\"" + test.entered + "\"",
|
||||
"canonical=\"" + test.canonical + "\"",
|
||||
} {
|
||||
if !strings.Contains(output.String(), fragment) {
|
||||
t.Errorf("output %q does not contain %q", output.String(), fragment)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobalHelpAliasDoesNotRunCoreExampleCommand(t *testing.T) {
|
||||
app, err := newApp()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"hello", "--hlep"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "--hlep → --help") || strings.Contains(output.String(), "Hello;") {
|
||||
t.Fatalf("help alias outcome/output = (%#v, %q)", outcome, output.String())
|
||||
}
|
||||
output.Reset()
|
||||
outcome = app.Execute(context.Background(), []string{"ws", "--help"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "greet") || strings.Contains(output.String(), "Hello;") {
|
||||
t.Fatalf("group shortcut help outcome/output = (%#v, %q)", outcome, output.String())
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,11 @@ go run . promptkit list profiles --config fixtures/after.yml --campaign autumn -
|
||||
go run . promptkit list backends --campaign spring
|
||||
go run . promptkit list namespaces --campaign spring
|
||||
go run . promptkit list collections --campaign spring
|
||||
go run . list collections --campaign spring
|
||||
go run . pk list bundles --campaign spring
|
||||
go run . promptkit list prompts --namespace field-notes --campaign spring
|
||||
go run . promptkit show prompt briefing --campaign spring --format yaml
|
||||
go run . pk show prompt briefing --campaign spring --fmt yaml
|
||||
go run . promptkit show profile spring --campaign spring
|
||||
go run . promptkit requirements show briefing --campaign spring
|
||||
go run . promptkit requirements show briefing --campaign spring --var mode=full --format yaml
|
||||
@@ -24,10 +27,19 @@ go run . promptkit requirements validate briefing --campaign spring --input topi
|
||||
go run . promptkit requirements validate briefing --campaign spring --input topic=fixtures/topic.txt --var mode=full --input notes=fixtures/notes.txt --format yaml
|
||||
go run . promptkit requirements validate briefing --campaign spring
|
||||
go run . promptkit test text_inference --campaign spring --prompt briefing --profile spring
|
||||
go run . smoke text_inference --campaign spring --prompt briefing --profile spring
|
||||
go run . promptkit test all --campaign spring --prompt briefing --profile spring
|
||||
go run . pk --hlep
|
||||
go run . session restore session-42 --campaign spring
|
||||
```
|
||||
|
||||
`newApp` installs application-owned command and flag aliases alongside the
|
||||
complete `ckconfig` and `ckprompt` declarations. `list collections` and
|
||||
`promptkit list bundles` both reach the canonical collection command; `pk`
|
||||
shortens the PromptKit group. `--fmt` is scoped to named prompt inspection and
|
||||
`--hlep` is available globally. The `smoke` shortcut retains the canonical
|
||||
`promptkit test KIND` operand, including the special `all` aggregate.
|
||||
|
||||
The sources and session store are small in-memory fixtures. `--campaign` is a
|
||||
typed selector shared by the app and PromptKit configuration scopes. Their
|
||||
`ckconfig.Suite[*inspectionInput, *runCache]` uses `NewInput` to initialize each
|
||||
@@ -92,3 +104,12 @@ required `topic` artifact is not supplied. The builder defaults variables from
|
||||
the selected campaign but leaves artifact bindings to the operator. The
|
||||
`requirements show` commands remain discovery reports: partial discovery can
|
||||
succeed even when a concrete request would fail validation.
|
||||
|
||||
The alias examples use the local CommandKit source because the composed
|
||||
module's published core dependency, v0.2.0, predates alias declarations. From
|
||||
the repository root, add the three local modules to `go.work` as described in
|
||||
the [developer guide](../../../../docs/development.md#documentation-validation),
|
||||
then run `go test ./...` from this example directory. This checks the example
|
||||
against the current local core and integration sources; it does not claim that
|
||||
published v0.2.0 supports aliases. The next core dependency adoption is handled
|
||||
after the core release is prepared.
|
||||
|
||||
@@ -200,6 +200,10 @@ func newApp() (*commandkit.App, error) {
|
||||
}
|
||||
|
||||
func newAppWithLLMClient(client promptkit.LLMClient) (*commandkit.App, error) {
|
||||
return newAppWithEngineLoader(client, loadPromptEngine)
|
||||
}
|
||||
|
||||
func newAppWithEngineLoader(client promptkit.LLMClient, engineLoader func(context.Context, ckconfig.Selection, *inspectionInput, *runCache) (*promptkit.Engine, error)) (*commandkit.App, error) {
|
||||
if client == nil {
|
||||
client = &exampleLLMClient{}
|
||||
}
|
||||
@@ -210,7 +214,7 @@ func newAppWithLLMClient(client promptkit.LLMClient) (*commandkit.App, error) {
|
||||
stateFactory := func() *runCache { return &runCache{llmClient: client} }
|
||||
|
||||
appScope := ckconfig.FromConfigKit("app", "Application settings.", loadApplicationConfig)
|
||||
engineScope := ckprompt.ConfigScope(loadPromptEngine)
|
||||
engineScope := ckprompt.ConfigScope(engineLoader)
|
||||
configCommand := ckconfig.Command(ckconfig.Suite[*inspectionInput, *runCache]{
|
||||
NewInput: inputFactory,
|
||||
NewState: stateFactory,
|
||||
@@ -227,12 +231,12 @@ func newAppWithLLMClient(client promptkit.LLMClient) (*commandkit.App, error) {
|
||||
Scopes: []ckconfig.Scope[*inspectionInput, *runCache]{appScope, engineScope},
|
||||
})
|
||||
|
||||
definitions := ckprompt.FromEngine(loadPromptEngine)
|
||||
definitions := ckprompt.FromEngine(engineLoader)
|
||||
definitions.NewInput = inputFactory
|
||||
definitions.NewState = stateFactory
|
||||
definitions.Selectors = []commandkit.Flag[*inspectionInput]{campaignSelector()}
|
||||
definitions = ckprompt.WithRequestValidation(definitions, loadPromptEngine, buildValidationRequest)
|
||||
definitions = ckprompt.WithSmokeTesting(definitions, loadPromptEngine, nil)
|
||||
definitions = ckprompt.WithRequestValidation(definitions, engineLoader, buildValidationRequest)
|
||||
definitions = ckprompt.WithSmokeTesting(definitions, engineLoader, nil)
|
||||
definitionCommand := ckprompt.Command(definitions)
|
||||
|
||||
store := map[string]session{
|
||||
@@ -270,12 +274,18 @@ func newAppWithLLMClient(client promptkit.LLMClient) (*commandkit.App, error) {
|
||||
return commandkit.Success()
|
||||
},
|
||||
})
|
||||
return commandkit.New(
|
||||
commandkit.Identity{Name: "composed-example", Version: "1.0"},
|
||||
declarations := []commandkit.Declaration{
|
||||
configCommand,
|
||||
definitionCommand,
|
||||
commandkit.Group("session", "Manage saved sessions.", restore),
|
||||
)
|
||||
commandkit.Alias("list collections", "promptkit list collections"),
|
||||
commandkit.Alias("promptkit list bundles", "promptkit list collections"),
|
||||
commandkit.Alias("pk", "promptkit"),
|
||||
commandkit.Alias("smoke", "promptkit test"),
|
||||
commandkit.FlagAliasFor("promptkit show prompt", "--fmt", "--format"),
|
||||
commandkit.FlagAlias("--hlep", "--help"),
|
||||
}
|
||||
return commandkit.New(commandkit.Identity{Name: "composed-example", Version: "1.0"}, declarations...)
|
||||
}
|
||||
|
||||
func parseString(raw string) (string, error) { return raw, nil }
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
@@ -350,6 +351,54 @@ func TestComposedCatalogsAndSmokeUseInjectedClientOnlyForInference(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComposedAliasesReachCanonicalCommands(t *testing.T) {
|
||||
clientCalls := 0
|
||||
engineLoads := 0
|
||||
engineLoader := func(ctx context.Context, selection ckconfig.Selection, input *inspectionInput, cache *runCache) (*promptkit.Engine, error) {
|
||||
engineLoads++
|
||||
return loadPromptEngine(ctx, selection, input, cache)
|
||||
}
|
||||
app, err := newAppWithEngineLoader(&exampleLLMClient{calls: &clientCalls}, engineLoader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"pk", "--hlep"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "--hlep → --help") || strings.Contains(output.String(), "field-notes") || engineLoads != 0 || clientCalls != 0 {
|
||||
t.Fatalf("group help alias outcome/output/engine loads/client calls = (%#v, %q, %d, %d)", outcome, output.String(), engineLoads, clientCalls)
|
||||
}
|
||||
|
||||
for _, args := range [][]string{
|
||||
{"list", "collections", "--campaign", "spring"},
|
||||
{"pk", "list", "bundles", "--campaign", "spring"},
|
||||
{"promptkit", "list", "collections", "--campaign", "spring"},
|
||||
} {
|
||||
output.Reset()
|
||||
outcome = app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "field-notes") {
|
||||
t.Fatalf("collection route %q outcome/report = (%#v, %q)", args, outcome, output.String())
|
||||
}
|
||||
}
|
||||
|
||||
for _, args := range [][]string{
|
||||
{"pk", "show", "prompt", "briefing", "--campaign", "spring", "--fmt", "yaml"},
|
||||
{"promptkit", "show", "prompt", "briefing", "--campaign", "spring", "--format", "yaml"},
|
||||
} {
|
||||
output.Reset()
|
||||
outcome = app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "prompt_id: briefing") {
|
||||
t.Fatalf("prompt inspection route %q outcome/report = (%#v, %q)", args, outcome, output.String())
|
||||
}
|
||||
}
|
||||
|
||||
output.Reset()
|
||||
outcome = app.Execute(context.Background(), []string{"smoke", "text_inference", "--campaign", "spring", "--prompt", "briefing", "--profile", "spring"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(output.String(), "kind: text_inference") || !strings.Contains(output.String(), "completed: true") || clientCalls != 1 {
|
||||
t.Fatalf("smoke shortcut outcome/report/client calls = (%#v, %q, %d)", outcome, output.String(), clientCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func composedReportValue(t *testing.T, data []byte, category string, fields ...string) *yaml.Node {
|
||||
t.Helper()
|
||||
var document yaml.Node
|
||||
|
||||
Reference in New Issue
Block a user