@lmctl-ai/lmctl 0.1.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.
Files changed (29) hide show
  1. package/LICENSE +36 -0
  2. package/README.md +36 -0
  3. package/bin/lmctl +4 -0
  4. package/dist/cli/index.js +2180 -0
  5. package/dist/cli/schema.sql +660 -0
  6. package/dist/cli/side_effect_classifier.json +19 -0
  7. package/dist/config/ai_test_templates/example-test.md.template +50 -0
  8. package/dist/config/ai_test_templates/index.md.template +27 -0
  9. package/dist/config/durable_memory_templates/index.md.template +29 -0
  10. package/dist/config/durable_memory_templates/skills_general.md.template +42 -0
  11. package/dist/config/durable_memory_templates/skills_lmdebug.md.template +31 -0
  12. package/dist/config/durable_memory_templates/skills_lmprobe.md.template +31 -0
  13. package/package.json +42 -0
  14. package/workflows/bugfix-extended-v2.compound.json +1018 -0
  15. package/workflows/bugfix-v2.compound.json +856 -0
  16. package/workflows/claim-check-spike-v2.compound.json +136 -0
  17. package/workflows/document-creation.compound.json +200 -0
  18. package/workflows/durable-memory-consolidation-v2.compound.json +185 -0
  19. package/workflows/example-v2.compound.json +200 -0
  20. package/workflows/image-qa.compound.json +128 -0
  21. package/workflows/index.jsonl +15 -0
  22. package/workflows/info-qa.compound.json +120 -0
  23. package/workflows/newspaper.compound.json +129 -0
  24. package/workflows/pr-fix.compound.json +183 -0
  25. package/workflows/pr-followup-v2.compound.json +969 -0
  26. package/workflows/provider-probe.compound.json +107 -0
  27. package/workflows/qa-suite.compound.json +186 -0
  28. package/workflows/spec-driven-task.compound.json +460 -0
  29. package/workflows/triage-v2.compound.json +721 -0
@@ -0,0 +1,107 @@
1
+ {
2
+ "schema_version": "lmctl/v3",
3
+ "definition_schema_version": 3,
4
+ "name": "provider-probe",
5
+ "version": 1,
6
+ "description": "Checks whether configured AI providers and team members can respond successfully in the current environment.",
7
+ "estimated_duration_ms": 30000,
8
+ "inputs": [
9
+ {
10
+ "name": "provider_team_path",
11
+ "type": "string",
12
+ "required": true,
13
+ "description": "Path to the provider research teamfile used for operator setup."
14
+ },
15
+ {
16
+ "name": "prompt",
17
+ "type": "string",
18
+ "required": true,
19
+ "description": "Probe prompt to send to the Researcher member."
20
+ }
21
+ ],
22
+ "outputs": [
23
+ {
24
+ "name": "answer",
25
+ "type": "string",
26
+ "source_step_id": "s-provider-probe-4d2a",
27
+ "source_field": "output",
28
+ "description": "Researcher answer captured from the probe."
29
+ }
30
+ ],
31
+ "steps": [
32
+ {
33
+ "id": "s-provider-probe-4d2a",
34
+ "name": "probe",
35
+ "type": "lmctl/interactive",
36
+ "typeVersion": 1,
37
+ "parameters": {
38
+ "termination": {
39
+ "operator_signal": true,
40
+ "agent_signal": "PROVIDER_PROBE_DONE",
41
+ "max_turns": 1
42
+ },
43
+ "artifact_name": "provider_probe_answer"
44
+ },
45
+ "members": {
46
+ "agent": {
47
+ "team": "default",
48
+ "alias": "Researcher",
49
+ "teamfile_path": "={{ context.inputs.provider_team_path }}",
50
+ "initial_prompt_template": "{{job_payload.prompt}}\n\nIf you can answer, include PROVIDER_PROBE_DONE at the end."
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "id": "s-terminal-done-7e42",
56
+ "name": "terminal:done",
57
+ "type": "lmctl/terminal",
58
+ "typeVersion": 1,
59
+ "parameters": {
60
+ "name": "done",
61
+ "kind": "success"
62
+ }
63
+ },
64
+ {
65
+ "id": "s-terminal-aborted-91be",
66
+ "name": "terminal:aborted",
67
+ "type": "lmctl/terminal",
68
+ "typeVersion": 1,
69
+ "parameters": {
70
+ "name": "aborted",
71
+ "kind": "failure"
72
+ }
73
+ }
74
+ ],
75
+ "connections": {
76
+ "s-provider-probe-4d2a": {
77
+ "done": [
78
+ [
79
+ {
80
+ "step_id": "s-terminal-done-7e42",
81
+ "type": "main",
82
+ "index": 0
83
+ }
84
+ ]
85
+ ],
86
+ "timeout": [
87
+ [
88
+ {
89
+ "step_id": "s-terminal-aborted-91be",
90
+ "type": "main",
91
+ "index": 0
92
+ }
93
+ ]
94
+ ],
95
+ "aborted": [
96
+ [
97
+ {
98
+ "step_id": "s-terminal-aborted-91be",
99
+ "type": "main",
100
+ "index": 0
101
+ }
102
+ ]
103
+ ]
104
+ }
105
+ },
106
+ "entry_step_id": "s-provider-probe-4d2a"
107
+ }
@@ -0,0 +1,186 @@
1
+ {
2
+ "schema_version": "lmctl/v3",
3
+ "definition_schema_version": 3,
4
+ "name": "qa-suite",
5
+ "version": 1,
6
+ "description": "Runs project QA chapters, interprets failures, creates project issues for failing chapters, and reports the open issue count.",
7
+ "estimated_duration_ms": 90000,
8
+ "errors": {
9
+ "on_unhandled_failure": {
10
+ "attention_kind": "workflow_failed",
11
+ "severity": "error",
12
+ "capture_evidence": true
13
+ }
14
+ },
15
+ "inputs": [
16
+ {
17
+ "name": "project_name",
18
+ "type": "string",
19
+ "required": true,
20
+ "description": "Project to test. The project's team must include Tester and Interpreter aliases. Different providers are recommended for cross-LLM checking."
21
+ },
22
+ {
23
+ "name": "chapter_filter",
24
+ "type": "string",
25
+ "required": false,
26
+ "description": "Optional substring filter applied to ai-test chapter paths."
27
+ },
28
+ {
29
+ "name": "retest_mode",
30
+ "type": "boolean",
31
+ "required": false,
32
+ "description": "Reserved for closed-issue retest mode; MVP still runs all matching chapters."
33
+ }
34
+ ],
35
+ "outputs": [
36
+ {
37
+ "name": "open_issue_count",
38
+ "type": "number",
39
+ "source_step_id": "s-aggregate-c9d0",
40
+ "source_field": "open_count",
41
+ "description": "Open project_issue count after qa-suite finishes."
42
+ }
43
+ ],
44
+ "steps": [
45
+ {
46
+ "id": "s-discover-chapters-a1b2",
47
+ "name": "discover_chapters",
48
+ "type": "lmctl/shell-step",
49
+ "typeVersion": 1,
50
+ "estimated_duration_ms": 5000,
51
+ "parameters": {
52
+ "command": "node -e 'const fs=require(\"fs\"), path=require(\"path\"); const root=path.join(process.argv[1],\"ai-test\"); const filter=process.argv[2] || \"\"; const files=fs.existsSync(root)?fs.readdirSync(root).filter((f)=>f.endsWith(\".md\")&&f!==\"index.md\").sort().map((f)=>\"ai-test/\"+f):[]; console.log(JSON.stringify(filter?files.filter((f)=>f.includes(filter)):files));' \"{{project.local_path}}\" \"={{ context.inputs.chapter_filter || '' }}\"",
53
+ "capture_output_to": "qa_suite_chapters",
54
+ "outcomes_from_exit": {
55
+ "0": "@terminal:exit_0",
56
+ "default": "@terminal:exit_default"
57
+ }
58
+ }
59
+ },
60
+ {
61
+ "id": "s-run-test-c3d4",
62
+ "name": "run_test",
63
+ "type": "lmctl/review",
64
+ "typeVersion": 1,
65
+ "estimated_duration_ms": 40000,
66
+ "parameters": {
67
+ "max_loops": 1
68
+ },
69
+ "members": {
70
+ "coder": {
71
+ "team": "default",
72
+ "alias": "Tester",
73
+ "prompt_template": "You are the QA Tester for project {{project.name}}.\n\nThe project has AI-driven test chapters under {{project.local_path}}/ai-test. Execute the Setup and Action sections for the matching chapters, compare observations against Expected, and summarize the result. The follow-up Interpreter agent will independently judge pass/fail.\n\nProject name: ={{ context.inputs.project_name }}\nChapter filter: ={{ context.inputs.chapter_filter || '' }}\n\nRequired team setup: this workflow expects aliases Tester and Interpreter on the project team. Different providers are recommended.\n\nEnd with exactly:\nSTANCE: tested\n\nIf the test environment is unavailable, end with:\nSTANCE: blocked"
74
+ },
75
+ "reviewers": []
76
+ }
77
+ },
78
+ {
79
+ "id": "s-interpret-e5f6",
80
+ "name": "interpret",
81
+ "type": "lmctl/review",
82
+ "typeVersion": 1,
83
+ "estimated_duration_ms": 30000,
84
+ "parameters": {
85
+ "max_loops": 1
86
+ },
87
+ "members": {
88
+ "coder": {
89
+ "team": "default",
90
+ "alias": "Interpreter",
91
+ "prompt_template": "You are the independent Interpreter for project {{project.name}}.\n\nReview the AI test corpus under {{project.local_path}}/ai-test and the Tester result from the prior workflow step if visible in the session. Decide the overall suite verdict:\n- pass: all matching chapters satisfy Expected\n- fail: at least one matching chapter fails Expected\n- inconclusive: evidence is insufficient or the test itself is ambiguous\n\nEnd with exactly one line:\nSTANCE: pass | fail | inconclusive"
92
+ },
93
+ "reviewers": []
94
+ }
95
+ },
96
+ {
97
+ "id": "s-create-issues-a7b8",
98
+ "name": "create_issues",
99
+ "type": "lmctl/shell-step",
100
+ "typeVersion": 1,
101
+ "estimated_duration_ms": 10000,
102
+ "parameters": {
103
+ "command": "node -e 'const fs=require(\"fs\"), path=require(\"path\"), cp=require(\"child_process\"); const project=process.argv[1], root=path.join(process.argv[2],\"ai-test\"), runId=process.argv[3], filter=process.argv[4]||\"\"; let created=0; if(fs.existsSync(root)){ for(const file of fs.readdirSync(root).filter((f)=>f.endsWith(\".md\")&&f!==\"index.md\").sort()){ const rel=\"ai-test/\"+file; if(filter && !rel.includes(filter)) continue; const full=path.join(root,file); const body=fs.readFileSync(full,\"utf8\"); if(!/EXPECTED_QA_STATUS:\\s*fail/.test(body)) continue; const title=\"AI test failed: \"+rel; const result=cp.spawnSync(\"lmctl\",[\"api\",\"--direct\",\"issues\",\"create\",project,\"--title\",title,\"--body\",body,\"--severity\",\"medium\",\"--labels\",\"[\\\"qa-suite\\\"]\",\"--ai-test-path\",rel,\"--source-run-id\",runId,\"--json\"],{encoding:\"utf8\",env:process.env}); if(result.status!==0){ process.stderr.write(result.stderr||result.stdout||\"issue create failed\"); process.exit(result.status||1); } created++; }} console.log(JSON.stringify({created}));' \"{{project.name}}\" \"{{project.local_path}}\" \"{{run_id}}\" \"={{ context.inputs.chapter_filter || '' }}\"",
104
+ "capture_output_to": "qa_suite_created_issues",
105
+ "outcomes_from_exit": {
106
+ "0": "@terminal:exit_0",
107
+ "default": "@terminal:exit_default"
108
+ }
109
+ }
110
+ },
111
+ {
112
+ "id": "s-aggregate-c9d0",
113
+ "name": "aggregate",
114
+ "type": "lmctl/shell-step",
115
+ "typeVersion": 1,
116
+ "estimated_duration_ms": 5000,
117
+ "parameters": {
118
+ "command": "node -e 'const cp=require(\"child_process\"); const project=process.argv[1]; const result=cp.spawnSync(\"lmctl\",[\"api\",\"--direct\",\"issues\",\"list\",project,\"--status\",\"open\",\"--json\"],{encoding:\"utf8\",env:process.env}); if(result.status!==0){ process.stderr.write(result.stderr||result.stdout||\"issues list failed\"); process.exit(result.status||1); } const rows=JSON.parse(result.stdout||\"[]\"); console.log(JSON.stringify({open_count:rows.length})); process.exit(rows.length===0?0:1);' \"{{project.name}}\"",
119
+ "capture_output_to": "qa_suite_open_issue_count",
120
+ "outcomes_from_exit": {
121
+ "0": "@terminal:exit_0",
122
+ "1": "@terminal:exit_1",
123
+ "default": "@terminal:exit_default"
124
+ }
125
+ }
126
+ },
127
+ {
128
+ "id": "s-terminal-clean-1111",
129
+ "name": "terminal:clean",
130
+ "type": "lmctl/terminal",
131
+ "typeVersion": 1,
132
+ "parameters": {
133
+ "name": "clean",
134
+ "kind": "success"
135
+ }
136
+ },
137
+ {
138
+ "id": "s-terminal-has-failures-2222",
139
+ "name": "terminal:has-failures",
140
+ "type": "lmctl/terminal",
141
+ "typeVersion": 1,
142
+ "parameters": {
143
+ "name": "has-failures",
144
+ "kind": "neutral"
145
+ }
146
+ },
147
+ {
148
+ "id": "s-terminal-aborted-3333",
149
+ "name": "terminal:aborted",
150
+ "type": "lmctl/terminal",
151
+ "typeVersion": 1,
152
+ "parameters": {
153
+ "name": "aborted",
154
+ "kind": "failure"
155
+ }
156
+ }
157
+ ],
158
+ "connections": {
159
+ "s-discover-chapters-a1b2": {
160
+ "exit_0": [[{ "step_id": "s-run-test-c3d4", "type": "main", "index": 0 }]],
161
+ "exit_default": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]]
162
+ },
163
+ "s-run-test-c3d4": {
164
+ "tested": [[{ "step_id": "s-interpret-e5f6", "type": "main", "index": 0 }]],
165
+ "blocked": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]],
166
+ "failed": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]]
167
+ },
168
+ "s-interpret-e5f6": {
169
+ "pass": [[{ "step_id": "s-aggregate-c9d0", "type": "main", "index": 0 }]],
170
+ "fail": [[{ "step_id": "s-create-issues-a7b8", "type": "main", "index": 0 }]],
171
+ "inconclusive": [[{ "step_id": "s-aggregate-c9d0", "type": "main", "index": 0 }]],
172
+ "blocked": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]],
173
+ "failed": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]]
174
+ },
175
+ "s-create-issues-a7b8": {
176
+ "exit_0": [[{ "step_id": "s-aggregate-c9d0", "type": "main", "index": 0 }]],
177
+ "exit_default": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]]
178
+ },
179
+ "s-aggregate-c9d0": {
180
+ "exit_0": [[{ "step_id": "s-terminal-clean-1111", "type": "main", "index": 0 }]],
181
+ "exit_1": [[{ "step_id": "s-terminal-has-failures-2222", "type": "main", "index": 0 }]],
182
+ "exit_default": [[{ "step_id": "s-terminal-aborted-3333", "type": "main", "index": 0 }]]
183
+ }
184
+ },
185
+ "entry_step_id": "s-discover-chapters-a1b2"
186
+ }