@dannylee1020/kkt 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +275 -0
- package/assets/kkt-readme-modern.png +0 -0
- package/bin/kkt-install.mjs +25 -0
- package/cmd/kkt/main.go +15 -0
- package/go.mod +3 -0
- package/internal/workflow/cli.go +349 -0
- package/internal/workflow/cli_test.go +1266 -0
- package/internal/workflow/guardrails.go +900 -0
- package/internal/workflow/init.go +164 -0
- package/internal/workflow/init_test.go +113 -0
- package/internal/workflow/operations.go +1855 -0
- package/internal/workflow/validation_commands.go +291 -0
- package/internal/workflow/workspace.go +802 -0
- package/internal/workflow/workspace_test.go +285 -0
- package/package.json +45 -0
- package/scripts/install-cli.sh +210 -0
- package/scripts/install.sh +644 -0
- package/skills/kkt/SKILL.md +74 -0
- package/skills/kkt/references/discovery-tooling.md +53 -0
- package/skills/kkt/references/feature-optimization-model.md +120 -0
- package/skills/kkt/references/kkt-kernel.md +58 -0
- package/skills/kkt/references/layered-modeling-methods.md +101 -0
- package/skills/kkt/references/plan-assimilation.md +46 -0
- package/skills/kkt/references/schemas.md +231 -0
- package/skills/kkt/references/state-contract.md +76 -0
- package/skills/kkt-loop/SKILL.md +99 -0
- package/skills/kkt-loop/references/discovery-tooling.md +53 -0
- package/skills/kkt-loop/references/feature-optimization-model.md +120 -0
- package/skills/kkt-loop/references/kkt-kernel.md +58 -0
- package/skills/kkt-loop/references/layered-modeling-methods.md +101 -0
- package/skills/kkt-loop/references/plan-assimilation.md +46 -0
- package/skills/kkt-loop/references/schemas.md +231 -0
- package/skills/kkt-loop/references/state-contract.md +76 -0
- package/skills/kkt-model/SKILL.md +76 -0
- package/skills/kkt-model/references/discovery-tooling.md +53 -0
- package/skills/kkt-model/references/feature-optimization-model.md +120 -0
- package/skills/kkt-model/references/kkt-kernel.md +58 -0
- package/skills/kkt-model/references/layered-modeling-methods.md +101 -0
- package/skills/kkt-model/references/plan-assimilation.md +46 -0
- package/skills/kkt-model/references/schemas.md +231 -0
- package/skills/kkt-model/references/state-contract.md +76 -0
- package/skills/kkt-run/SKILL.md +62 -0
- package/skills/kkt-run/references/discovery-tooling.md +53 -0
- package/skills/kkt-run/references/feature-optimization-model.md +120 -0
- package/skills/kkt-run/references/kkt-kernel.md +58 -0
- package/skills/kkt-run/references/layered-modeling-methods.md +101 -0
- package/skills/kkt-run/references/plan-assimilation.md +46 -0
- package/skills/kkt-run/references/schemas.md +231 -0
- package/skills/kkt-run/references/state-contract.md +76 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
package workflow
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"errors"
|
|
5
|
+
"fmt"
|
|
6
|
+
"io"
|
|
7
|
+
"os"
|
|
8
|
+
"strings"
|
|
9
|
+
"time"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
var Version = "dev"
|
|
13
|
+
|
|
14
|
+
const usageText = `KKT Workflow CLI
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
kkt --version
|
|
18
|
+
kkt start plan|model|run|loop <request>
|
|
19
|
+
kkt status [--json] [path]
|
|
20
|
+
kkt next [--json] [path]
|
|
21
|
+
kkt show [artifact]
|
|
22
|
+
kkt intent|discovery|model [--method method] [content]
|
|
23
|
+
kkt plan|progress [content]
|
|
24
|
+
kkt evidence [--for criterion] [--command command] [content]
|
|
25
|
+
kkt notes [content]
|
|
26
|
+
kkt guardrails show|set|validate [content]
|
|
27
|
+
kkt judge --checkpoint checkpoint [--json] [path]
|
|
28
|
+
kkt run from-model [model-workspace]
|
|
29
|
+
kkt approve [scope]
|
|
30
|
+
kkt criteria [add|satisfy|block] [criterion]
|
|
31
|
+
kkt task [add|start|done|skip|block] [task]
|
|
32
|
+
kkt block [reason]
|
|
33
|
+
kkt validate [--run] [--timeout duration] [path]
|
|
34
|
+
kkt done [summary]
|
|
35
|
+
kkt resume [path]
|
|
36
|
+
kkt replay --check [path]
|
|
37
|
+
kkt uninstall [codex|claude|opencode|pi|all]
|
|
38
|
+
|
|
39
|
+
KKT skills own the workflow. This CLI handles deterministic .kkt state
|
|
40
|
+
scaffolding, workflow state, artifacts, evidence, validation, and cleanup.
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
func Run(args []string, stdout, stderr io.Writer) error {
|
|
44
|
+
if len(args) == 0 || args[0] == "help" || args[0] == "--help" {
|
|
45
|
+
fmt.Fprint(stdout, usageText)
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
if args[0] == "--version" {
|
|
49
|
+
fmt.Fprintf(stdout, "kkt %s\n", Version)
|
|
50
|
+
return nil
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
switch args[0] {
|
|
54
|
+
case "start":
|
|
55
|
+
return runStart(args[1:], stdout)
|
|
56
|
+
case "status":
|
|
57
|
+
return runStatus(args[1:], stdout)
|
|
58
|
+
case "next":
|
|
59
|
+
return runNext(args[1:], stdout)
|
|
60
|
+
case "show":
|
|
61
|
+
return runShow(args[1:], stdout)
|
|
62
|
+
case "intent", "discovery", "model", "plan", "progress", "evidence", "notes":
|
|
63
|
+
return runArtifact(args[0], args[1:], stdout)
|
|
64
|
+
case "guardrails":
|
|
65
|
+
return runGuardrails(args[1:], stdout)
|
|
66
|
+
case "judge":
|
|
67
|
+
return runJudge(args[1:], stdout)
|
|
68
|
+
case "run":
|
|
69
|
+
return runRun(args[1:], stdout)
|
|
70
|
+
case "approve":
|
|
71
|
+
return runApprove(args[1:], stdout)
|
|
72
|
+
case "criteria":
|
|
73
|
+
return runCriteria(args[1:], stdout)
|
|
74
|
+
case "task":
|
|
75
|
+
return runTask(args[1:], stdout)
|
|
76
|
+
case "block":
|
|
77
|
+
return runBlock(args[1:], stdout)
|
|
78
|
+
case "validate":
|
|
79
|
+
return runValidate(args[1:], stdout)
|
|
80
|
+
case "done":
|
|
81
|
+
return runDone(args[1:], stdout)
|
|
82
|
+
case "resume":
|
|
83
|
+
return runResume(args[1:], stdout)
|
|
84
|
+
case "replay":
|
|
85
|
+
return runReplay(args[1:], stdout)
|
|
86
|
+
case "uninstall":
|
|
87
|
+
return runUninstall(args[1:], stdout)
|
|
88
|
+
default:
|
|
89
|
+
return fmt.Errorf("unsupported command %q\n\n%s", args[0], usageText)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func runUninstall(args []string, stdout io.Writer) error {
|
|
94
|
+
if err := rejectFlags("uninstall", args); err != nil {
|
|
95
|
+
return err
|
|
96
|
+
}
|
|
97
|
+
if len(args) > 1 {
|
|
98
|
+
return errors.New("uninstall accepts at most one agent: codex, claude, opencode, pi, or all")
|
|
99
|
+
}
|
|
100
|
+
agent := strings.TrimSpace(firstArg(args))
|
|
101
|
+
if agent == "" {
|
|
102
|
+
agent = "all"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
plans, err := UninstallPlans(agent)
|
|
106
|
+
if err != nil {
|
|
107
|
+
return err
|
|
108
|
+
}
|
|
109
|
+
for _, plan := range plans {
|
|
110
|
+
fmt.Fprintf(stdout, "target: %s\n", plan.Agent)
|
|
111
|
+
fmt.Fprintf(stdout, "file: %s\n", plan.Path)
|
|
112
|
+
changed, err := RemoveInstruction(plan.Path)
|
|
113
|
+
if err != nil {
|
|
114
|
+
return err
|
|
115
|
+
}
|
|
116
|
+
if changed {
|
|
117
|
+
fmt.Fprintln(stdout, "removed")
|
|
118
|
+
} else {
|
|
119
|
+
fmt.Fprintln(stdout, "already current")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
executable, err := os.Executable()
|
|
124
|
+
if err != nil {
|
|
125
|
+
return err
|
|
126
|
+
}
|
|
127
|
+
fmt.Fprintf(stdout, "binary: %s\n", executable)
|
|
128
|
+
if err := os.Remove(executable); err != nil {
|
|
129
|
+
if os.IsNotExist(err) {
|
|
130
|
+
fmt.Fprintln(stdout, "already current")
|
|
131
|
+
return nil
|
|
132
|
+
}
|
|
133
|
+
return err
|
|
134
|
+
}
|
|
135
|
+
fmt.Fprintln(stdout, "removed")
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func runStart(args []string, stdout io.Writer) error {
|
|
140
|
+
if err := rejectFlags("start", args); err != nil {
|
|
141
|
+
return err
|
|
142
|
+
}
|
|
143
|
+
if len(args) < 2 {
|
|
144
|
+
return errors.New("start requires a profile and request: plan, model, run, or loop")
|
|
145
|
+
}
|
|
146
|
+
selectedProfile := strings.TrimSpace(args[0])
|
|
147
|
+
request := strings.TrimSpace(strings.Join(args[1:], " "))
|
|
148
|
+
if request == "" {
|
|
149
|
+
return errors.New("start requires a request")
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
workspace, err := StartWorkflow(".", request, selectedProfile)
|
|
153
|
+
if err != nil {
|
|
154
|
+
return err
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fmt.Fprintf(stdout, "created: %s\n", workspace.Path)
|
|
158
|
+
fmt.Fprintf(stdout, "profile: %s\n", workspace.Profile)
|
|
159
|
+
fmt.Fprintf(stdout, "next: %s\n", startInstruction(workspace.Profile))
|
|
160
|
+
return nil
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func runStatus(args []string, stdout io.Writer) error {
|
|
164
|
+
jsonOutput, path, err := parseJSONPathArgs("status", args)
|
|
165
|
+
if err != nil {
|
|
166
|
+
return err
|
|
167
|
+
}
|
|
168
|
+
workspace, err := ResolveWorkspace(".", path)
|
|
169
|
+
if err != nil {
|
|
170
|
+
return err
|
|
171
|
+
}
|
|
172
|
+
state, err := ReadState(workspace)
|
|
173
|
+
if err != nil {
|
|
174
|
+
return err
|
|
175
|
+
}
|
|
176
|
+
report, err := statusReport(workspace, state)
|
|
177
|
+
if err != nil {
|
|
178
|
+
return err
|
|
179
|
+
}
|
|
180
|
+
if jsonOutput {
|
|
181
|
+
return writeJSON(stdout, report)
|
|
182
|
+
}
|
|
183
|
+
fmt.Fprintf(stdout, "workspace: %s\n", workspace)
|
|
184
|
+
fmt.Fprintf(stdout, "status: %s\n", state.Status)
|
|
185
|
+
fmt.Fprintf(stdout, "active_layer: %s\n", state.ActiveLayer)
|
|
186
|
+
fmt.Fprintf(stdout, "profile: %s\n", state.Profile)
|
|
187
|
+
fmt.Fprintf(stdout, "approval: %s\n", state.ApprovalStatus)
|
|
188
|
+
if report.CurrentTask != "" {
|
|
189
|
+
fmt.Fprintf(stdout, "current_task: %s\n", report.CurrentTask)
|
|
190
|
+
}
|
|
191
|
+
if report.Validation.OK {
|
|
192
|
+
fmt.Fprintln(stdout, "validation: valid")
|
|
193
|
+
} else {
|
|
194
|
+
fmt.Fprintln(stdout, "validation: invalid")
|
|
195
|
+
for _, issue := range report.Validation.Issues {
|
|
196
|
+
fmt.Fprintf(stdout, "- %s\n", issue)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if report.StaleComplete {
|
|
200
|
+
fmt.Fprintln(stdout, "stored_status: complete is stale")
|
|
201
|
+
}
|
|
202
|
+
return nil
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func runNext(args []string, stdout io.Writer) error {
|
|
206
|
+
jsonOutput, path, err := parseJSONPathArgs("next", args)
|
|
207
|
+
if err != nil {
|
|
208
|
+
return err
|
|
209
|
+
}
|
|
210
|
+
workspace, err := ResolveWorkspace(".", path)
|
|
211
|
+
if err != nil {
|
|
212
|
+
return err
|
|
213
|
+
}
|
|
214
|
+
state, err := ReadState(workspace)
|
|
215
|
+
if err != nil {
|
|
216
|
+
return err
|
|
217
|
+
}
|
|
218
|
+
action := nextActionForWorkspace(workspace, state)
|
|
219
|
+
if jsonOutput {
|
|
220
|
+
return writeJSON(stdout, action)
|
|
221
|
+
}
|
|
222
|
+
fmt.Fprintln(stdout, action.Instruction)
|
|
223
|
+
return nil
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
func runValidate(args []string, stdout io.Writer) error {
|
|
227
|
+
options, err := parseValidateArgs(args)
|
|
228
|
+
if err != nil {
|
|
229
|
+
return err
|
|
230
|
+
}
|
|
231
|
+
workspace, err := ResolveWorkspace(".", options.Path)
|
|
232
|
+
if err != nil {
|
|
233
|
+
return err
|
|
234
|
+
}
|
|
235
|
+
if options.Run {
|
|
236
|
+
if err := RunRequiredValidationCommands(".", workspace, options.Timeout, stdout); err != nil {
|
|
237
|
+
return err
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
result, err := ValidateWorkspace(workspace)
|
|
241
|
+
if err != nil {
|
|
242
|
+
return err
|
|
243
|
+
}
|
|
244
|
+
if result.OK {
|
|
245
|
+
if err := markValidationLayerComplete(workspace); err != nil {
|
|
246
|
+
return err
|
|
247
|
+
}
|
|
248
|
+
if err := appendValidationEvent(workspace, result); err != nil {
|
|
249
|
+
return err
|
|
250
|
+
}
|
|
251
|
+
fmt.Fprintf(stdout, "valid: %s\n", workspace)
|
|
252
|
+
return nil
|
|
253
|
+
}
|
|
254
|
+
if err := appendValidationEvent(workspace, result); err != nil {
|
|
255
|
+
return err
|
|
256
|
+
}
|
|
257
|
+
fmt.Fprintf(stdout, "invalid: %s\n", workspace)
|
|
258
|
+
for _, issue := range result.Issues {
|
|
259
|
+
fmt.Fprintf(stdout, "- %s\n", issue)
|
|
260
|
+
}
|
|
261
|
+
return errors.New("workspace validation failed")
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
type validateOptions struct {
|
|
265
|
+
Run bool
|
|
266
|
+
Timeout time.Duration
|
|
267
|
+
Path string
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
func parseValidateArgs(args []string) (validateOptions, error) {
|
|
271
|
+
options := validateOptions{Timeout: 10 * time.Minute}
|
|
272
|
+
for i := 0; i < len(args); i++ {
|
|
273
|
+
arg := args[i]
|
|
274
|
+
switch arg {
|
|
275
|
+
case "--run":
|
|
276
|
+
options.Run = true
|
|
277
|
+
case "--timeout":
|
|
278
|
+
i++
|
|
279
|
+
if i >= len(args) {
|
|
280
|
+
return validateOptions{}, errors.New("validate --timeout requires a duration")
|
|
281
|
+
}
|
|
282
|
+
timeout, err := time.ParseDuration(args[i])
|
|
283
|
+
if err != nil {
|
|
284
|
+
return validateOptions{}, fmt.Errorf("invalid validate timeout %q: %w", args[i], err)
|
|
285
|
+
}
|
|
286
|
+
if timeout <= 0 {
|
|
287
|
+
return validateOptions{}, errors.New("validate timeout must be greater than zero")
|
|
288
|
+
}
|
|
289
|
+
options.Timeout = timeout
|
|
290
|
+
default:
|
|
291
|
+
if strings.HasPrefix(arg, "-") {
|
|
292
|
+
return validateOptions{}, fmt.Errorf("validate does not accept flag: %s", arg)
|
|
293
|
+
}
|
|
294
|
+
if options.Path != "" {
|
|
295
|
+
return validateOptions{}, errors.New("validate accepts at most one path")
|
|
296
|
+
}
|
|
297
|
+
options.Path = arg
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return options, nil
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
func rejectFlags(command string, args []string) error {
|
|
304
|
+
for _, arg := range args {
|
|
305
|
+
if strings.HasPrefix(arg, "-") {
|
|
306
|
+
return fmt.Errorf("%s does not accept flags: %s", command, arg)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return nil
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
func parseJSONPathArgs(command string, args []string) (bool, string, error) {
|
|
313
|
+
jsonOutput := false
|
|
314
|
+
path := ""
|
|
315
|
+
for _, arg := range args {
|
|
316
|
+
if arg == "--json" {
|
|
317
|
+
jsonOutput = true
|
|
318
|
+
continue
|
|
319
|
+
}
|
|
320
|
+
if strings.HasPrefix(arg, "-") {
|
|
321
|
+
return false, "", fmt.Errorf("%s does not accept flag: %s", command, arg)
|
|
322
|
+
}
|
|
323
|
+
if path != "" {
|
|
324
|
+
return false, "", fmt.Errorf("%s accepts at most one path", command)
|
|
325
|
+
}
|
|
326
|
+
path = arg
|
|
327
|
+
}
|
|
328
|
+
return jsonOutput, path, nil
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func firstArg(args []string) string {
|
|
332
|
+
if len(args) == 0 {
|
|
333
|
+
return ""
|
|
334
|
+
}
|
|
335
|
+
return args[0]
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
func startInstruction(profile string) string {
|
|
339
|
+
switch profile {
|
|
340
|
+
case "plan":
|
|
341
|
+
return "inspect relevant code/docs, record objective_function, files_to_modify, constraint_functions, decision_variables, and validation_proof with kkt model, then request approval before edits"
|
|
342
|
+
case "model":
|
|
343
|
+
return "record adaptive intent with kkt intent --method <method>, then inspect relevant code/docs and record discovery"
|
|
344
|
+
case "run":
|
|
345
|
+
return "record or import the selected model, run kkt judge --checkpoint model-ready, then request approval before edits"
|
|
346
|
+
default:
|
|
347
|
+
return "record adaptive intent with kkt intent --method <method>, then inspect relevant code/docs and record discovery/model/plan"
|
|
348
|
+
}
|
|
349
|
+
}
|