legion-cc 0.1.0 → 0.2.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/README.md +17 -26
- package/VERSION +1 -1
- package/agents/legion-orchestrator.md +4 -4
- package/bin/install.js +1 -1
- package/bin/legion-tools.cjs +34 -2
- package/bin/lib/config.cjs +3 -3
- package/bin/lib/core.cjs +16 -6
- package/bin/lib/init.cjs +20 -27
- package/bin/lib/session.cjs +4 -4
- package/bin/lib/state.cjs +2 -2
- package/bin/lib/version-check.cjs +183 -0
- package/commands/legion/devops/architect.md +1 -1
- package/commands/legion/devops/{build.md → init.md} +7 -7
- package/commands/legion/devops/quick.md +2 -2
- package/commands/legion/resume.md +2 -2
- package/commands/legion/status.md +2 -2
- package/hooks/legion-context-monitor.js +21 -0
- package/hooks/legion-statusline.js +32 -7
- package/package.json +1 -1
- package/references/agent-routing.md +3 -3
- package/references/devops/agent-map.md +3 -3
- package/references/devops/pipeline-patterns.md +3 -3
- package/references/domain-registry.md +2 -2
- package/references/ui-brand.md +4 -4
- package/templates/task-record.md +1 -1
- package/workflows/core/completion.md +4 -4
- package/workflows/core/context-load.md +21 -25
- package/workflows/core/init.md +3 -5
- package/workflows/devops/architect.md +3 -3
- package/workflows/devops/cycle.md +7 -7
- package/workflows/devops/execute.md +4 -4
- package/workflows/devops/{build.md → init.md} +14 -14
- package/workflows/devops/plan.md +5 -5
- package/workflows/devops/quick.md +9 -9
- package/workflows/devops/review.md +2 -2
- package/workflows/resume.md +3 -3
- package/workflows/status.md +4 -4
package/README.md
CHANGED
|
@@ -12,10 +12,10 @@ The first domain ships with Legion: **devops** — infrastructure design, planni
|
|
|
12
12
|
Each domain defines a sequential pipeline of specialized agents. The devops domain runs: `architect → plan → execute → review`. Every stage produces a numbered artifact that feeds the next.
|
|
13
13
|
|
|
14
14
|
**Fast context-aware execution**
|
|
15
|
-
`/legion:devops:quick` reads `.
|
|
15
|
+
`/legion:devops:quick` reads `.legion/codebase/` before dispatching to classify and route the task to the correct agent automatically.
|
|
16
16
|
|
|
17
17
|
**State tracking across sessions**
|
|
18
|
-
All pipeline state is written to `.planning/
|
|
18
|
+
All pipeline state is written to `.legion/planning/STATE.md` — current domain, stage, task history, and artifact chain. State persists between Claude Code sessions.
|
|
19
19
|
|
|
20
20
|
**Context window monitoring**
|
|
21
21
|
A `PostToolUse` hook reads context metrics after each tool call. When remaining context drops below 35%, the agent receives an in-context warning to wrap up. At 25%, it receives a critical stop directive. Warnings are debounced across tool calls and coexist safely with GSD.
|
|
@@ -24,7 +24,7 @@ A `PostToolUse` hook reads context metrics after each tool call. When remaining
|
|
|
24
24
|
`/legion:devops:cycle` pauses after the architect and plan stages to show the output and ask for approval before continuing. The user can approve, request a revision, or stop and save state for later.
|
|
25
25
|
|
|
26
26
|
**Sequential artifact management**
|
|
27
|
-
Each pipeline stage writes a numbered artifact to `.planning/
|
|
27
|
+
Each pipeline stage writes a numbered artifact to `.legion/planning/devops/`:
|
|
28
28
|
```
|
|
29
29
|
001-architect-eks-migration.md
|
|
30
30
|
002-plan-eks-migration.md
|
|
@@ -40,10 +40,7 @@ Each pipeline stage writes a numbered artifact to `.planning/legion/devops/`:
|
|
|
40
40
|
## Quick Start
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
44
|
-
cd legion
|
|
45
|
-
chmod +x install.sh
|
|
46
|
-
./install.sh
|
|
43
|
+
npx legion-cc@latest
|
|
47
44
|
```
|
|
48
45
|
|
|
49
46
|
The installer copies commands, workflows, templates, references, and hooks into `~/.claude/`, registers the `PostToolUse` context monitor in Claude Code settings, and writes a file manifest for clean uninstalls.
|
|
@@ -51,14 +48,9 @@ The installer copies commands, workflows, templates, references, and hooks into
|
|
|
51
48
|
**Options**
|
|
52
49
|
|
|
53
50
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
**Uninstall**
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
./uninstall.sh
|
|
51
|
+
npx legion-cc@latest --force # overwrite existing agent files
|
|
52
|
+
npx legion-cc@latest --dry-run # preview what would be installed
|
|
53
|
+
npx legion-cc@latest --uninstall # remove Legion
|
|
62
54
|
```
|
|
63
55
|
|
|
64
56
|
---
|
|
@@ -69,11 +61,11 @@ The installer copies commands, workflows, templates, references, and hooks into
|
|
|
69
61
|
|
|
70
62
|
| Command | Description |
|
|
71
63
|
|---------|-------------|
|
|
72
|
-
| `/legion:devops:quick <task>` | Classify the task and dispatch to the right agent. Reads `.codebase/` first. |
|
|
64
|
+
| `/legion:devops:quick <task>` | Classify the task and dispatch to the right agent. Reads `.legion/codebase/` first. |
|
|
73
65
|
| `/legion:devops:architect <task>` | Design infrastructure architecture with the `devops-architect` agent (opus). |
|
|
74
66
|
| `/legion:devops:plan [path]` | Decompose architecture into an implementation plan with `delivery-planner` (sonnet). Uses latest architect artifact if no path given. |
|
|
75
67
|
| `/legion:devops:execute [path]` | Implement the plan with `infra-executor` (opus). Supports `--task T1` to run a single task. |
|
|
76
|
-
| `/legion:devops:
|
|
68
|
+
| `/legion:devops:init [path]` | Scaffold project structure and create `.legion/codebase/` documentation with `codebase-builder` (opus). |
|
|
77
69
|
| `/legion:devops:review [path]` | Review changes against architectural intent with `devops-architect` in review mode (sonnet). |
|
|
78
70
|
| `/legion:devops:cycle <task>` | Run the full pipeline (architect → plan → execute → review) with checkpoints after architect and plan. |
|
|
79
71
|
|
|
@@ -113,8 +105,6 @@ legion/
|
|
|
113
105
|
├── workflows/ # Step-by-step execution logic referenced by commands
|
|
114
106
|
│ ├── core/ # init, context-load, completion (shared across domains)
|
|
115
107
|
│ └── devops/ # One workflow per command
|
|
116
|
-
├── install.sh
|
|
117
|
-
├── uninstall.sh
|
|
118
108
|
├── package.json
|
|
119
109
|
└── VERSION
|
|
120
110
|
```
|
|
@@ -128,7 +118,7 @@ legion/
|
|
|
128
118
|
| `devops-architect` | Senior/Lead DevOps Architect — infrastructure design, security assessment, migration planning, HA/DR | opus (architect) / sonnet (review) | `architect`, `review`, `cycle` |
|
|
129
119
|
| `delivery-planner` | Senior Delivery Planner — work decomposition, phased planning, dependency mapping, risk assessment | sonnet | `plan`, `cycle` |
|
|
130
120
|
| `infra-executor` | Elite Infrastructure Execution Specialist — Terraform, CI/CD, Kubernetes, AWS resource management | opus | `execute`, `cycle` |
|
|
131
|
-
| `codebase-builder` | Elite Codebase Builder — project scaffolding, `.codebase/` documentation creation, existing project analysis | opus | `
|
|
121
|
+
| `codebase-builder` | Elite Codebase Builder — project scaffolding, `.legion/codebase/` documentation creation, existing project analysis | opus | `init` |
|
|
132
122
|
|
|
133
123
|
The `devops-architect` agent doubles as reviewer: the same agent definition is invoked with a review prompt at sonnet model for the review stage.
|
|
134
124
|
|
|
@@ -166,7 +156,7 @@ The `devops-architect` agent doubles as reviewer: the same agent definition is i
|
|
|
166
156
|
done
|
|
167
157
|
```
|
|
168
158
|
|
|
169
|
-
Checkpoints default to ON after architect and plan, OFF after execute and review. This is configurable per project in `.planning/
|
|
159
|
+
Checkpoints default to ON after architect and plan, OFF after execute and review. This is configurable per project in `.legion/planning/config.json`.
|
|
170
160
|
|
|
171
161
|
---
|
|
172
162
|
|
|
@@ -187,9 +177,9 @@ Checkpoints default to ON after architect and plan, OFF after execute and review
|
|
|
187
177
|
/legion:devops:quick "Add ingress rule for port 8443 to eks-node SG"
|
|
188
178
|
```
|
|
189
179
|
|
|
190
|
-
**Documentation first** — existing project without `.codebase/` docs:
|
|
180
|
+
**Documentation first** — existing project without `.legion/codebase/` docs:
|
|
191
181
|
```
|
|
192
|
-
/legion:devops:
|
|
182
|
+
/legion:devops:init
|
|
193
183
|
/legion:devops:architect "migration plan"
|
|
194
184
|
```
|
|
195
185
|
|
|
@@ -210,7 +200,7 @@ Legion is designed to host multiple domains. To add a new domain (e.g., `backend
|
|
|
210
200
|
4. Create references at `references/{domain}/` — `agent-map.md` and `pipeline-patterns.md`
|
|
211
201
|
5. Define agents in `agents/` and register them in `references/{domain}/agent-map.md`
|
|
212
202
|
6. Add the domain entry to `references/domain-registry.md`
|
|
213
|
-
7. Run
|
|
203
|
+
7. Run `npx legion-cc --force` to deploy
|
|
214
204
|
|
|
215
205
|
Each domain must define: pipeline stages (ordered), agent mapping (stage to agent), model assignment, and classification rules for its `/quick` command.
|
|
216
206
|
|
|
@@ -247,8 +237,9 @@ Per-project state is written alongside your code:
|
|
|
247
237
|
|
|
248
238
|
```
|
|
249
239
|
{project}/
|
|
250
|
-
└── .
|
|
251
|
-
|
|
240
|
+
└── .legion/
|
|
241
|
+
├── codebase/ # Project documentation
|
|
242
|
+
└── planning/
|
|
252
243
|
├── STATE.md
|
|
253
244
|
├── config.json
|
|
254
245
|
├── sessions/
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0
|
|
@@ -14,7 +14,7 @@ You are the Legion Orchestrator — a lightweight routing agent that analyzes in
|
|
|
14
14
|
|
|
15
15
|
You do NOT execute tasks yourself. You:
|
|
16
16
|
1. Analyze the task description
|
|
17
|
-
2. Load project context (.
|
|
17
|
+
2. Load project context (.legion/)
|
|
18
18
|
3. Classify the task type
|
|
19
19
|
4. Recommend the appropriate agent and provide context for spawning
|
|
20
20
|
|
|
@@ -37,10 +37,10 @@ Analyze the task and classify into one of these categories:
|
|
|
37
37
|
**Triggers**: Writing Terraform code, modifying infrastructure, fixing configs, updating pipelines, creating AWS resources, changing security groups
|
|
38
38
|
**Keywords**: implement, create, add, write, fix, update, change, terraform, pipeline, module, resource, deploy, configure
|
|
39
39
|
|
|
40
|
-
###
|
|
40
|
+
### init
|
|
41
41
|
**Agent**: codebase-builder (opus)
|
|
42
|
-
**Triggers**: New project setup, scaffolding, creating .codebase/ documentation, initializing project structure
|
|
43
|
-
**Keywords**: scaffold, init, setup, create project, new project, .codebase/, document structure, bootstrap
|
|
42
|
+
**Triggers**: New project setup, scaffolding, creating .legion/codebase/ documentation, initializing project structure
|
|
43
|
+
**Keywords**: scaffold, init, setup, create project, new project, .legion/codebase/, document structure, bootstrap
|
|
44
44
|
|
|
45
45
|
### review
|
|
46
46
|
**Agent**: devops-architect (sonnet, review mode)
|
package/bin/install.js
CHANGED
|
@@ -784,7 +784,7 @@ function doUninstall() {
|
|
|
784
784
|
console.log(` ${c(yellow, 'Failed:')} ${failedCount} files`);
|
|
785
785
|
}
|
|
786
786
|
console.log('');
|
|
787
|
-
console.log(` ${c(dim, 'Note: Agent files (agents/) and project data (.
|
|
787
|
+
console.log(` ${c(dim, 'Note: Agent files (agents/) and project data (.legion/) were NOT removed.')}`);
|
|
788
788
|
console.log('');
|
|
789
789
|
}
|
|
790
790
|
|
package/bin/legion-tools.cjs
CHANGED
|
@@ -26,8 +26,9 @@ function usage() {
|
|
|
26
26
|
' generate-slug <text> Generate a URL-safe slug from text',
|
|
27
27
|
' current-timestamp [format] Print current timestamp (iso|date|datetime)',
|
|
28
28
|
' task-record <type> <desc> <status> Add a task record to STATE.md',
|
|
29
|
-
' context-load [path] Load context from .codebase/ and .planning/',
|
|
29
|
+
' context-load [path] Load context from .legion/codebase/ and .legion/planning/',
|
|
30
30
|
' domain list|info [name] Domain registry operations',
|
|
31
|
+
' update-check Check for newer version on npm',
|
|
31
32
|
'',
|
|
32
33
|
'Examples:',
|
|
33
34
|
' legion-tools init devops',
|
|
@@ -67,7 +68,7 @@ function die(msg, code) {
|
|
|
67
68
|
function requirePlanningDir() {
|
|
68
69
|
const planningDir = core.resolvePlanningDir();
|
|
69
70
|
if (!planningDir) {
|
|
70
|
-
die('No .planning/
|
|
71
|
+
die('No .legion/planning/ directory found. Run "legion-tools init <domain>" first.');
|
|
71
72
|
}
|
|
72
73
|
return planningDir;
|
|
73
74
|
}
|
|
@@ -376,6 +377,34 @@ function cmdDomain(args) {
|
|
|
376
377
|
}
|
|
377
378
|
}
|
|
378
379
|
|
|
380
|
+
// ─── Command: update-check ───────────────────────────────────────────────
|
|
381
|
+
|
|
382
|
+
function cmdUpdateCheck() {
|
|
383
|
+
const vc = require('./lib/version-check.cjs');
|
|
384
|
+
const current = vc.getInstalledVersion();
|
|
385
|
+
|
|
386
|
+
console.log(` Installed: v${current}`);
|
|
387
|
+
console.log(' Checking npm registry...');
|
|
388
|
+
|
|
389
|
+
const result = vc.checkForUpdate();
|
|
390
|
+
|
|
391
|
+
if (!result) {
|
|
392
|
+
console.log(` ${core.UI.warn} Could not reach npm registry.`);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (result.available) {
|
|
397
|
+
console.log(` ${core.UI.diamond} Update available: v${result.current} \u2192 v${result.latest}`);
|
|
398
|
+
console.log('');
|
|
399
|
+
console.log(' Run: npx legion-cc@latest');
|
|
400
|
+
} else {
|
|
401
|
+
console.log(` ${core.UI.check} Already on latest version (v${result.current})`);
|
|
402
|
+
}
|
|
403
|
+
console.log('');
|
|
404
|
+
|
|
405
|
+
out(result);
|
|
406
|
+
}
|
|
407
|
+
|
|
379
408
|
// ─── Main Router ─────────────────────────────────────────────────────────────
|
|
380
409
|
|
|
381
410
|
function main() {
|
|
@@ -413,6 +442,9 @@ function main() {
|
|
|
413
442
|
case 'domain':
|
|
414
443
|
cmdDomain(commandArgs);
|
|
415
444
|
break;
|
|
445
|
+
case 'update-check':
|
|
446
|
+
cmdUpdateCheck();
|
|
447
|
+
break;
|
|
416
448
|
default:
|
|
417
449
|
die(`Unknown command: ${command}. Run "legion-tools --help" for usage.`);
|
|
418
450
|
}
|
package/bin/lib/config.cjs
CHANGED
|
@@ -88,7 +88,7 @@ const DOMAIN_DEFAULTS = {
|
|
|
88
88
|
/**
|
|
89
89
|
* Load config.json from the given planning directory.
|
|
90
90
|
*
|
|
91
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
91
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
92
92
|
* @returns {object|null} Parsed config or null if missing/invalid
|
|
93
93
|
*/
|
|
94
94
|
function loadConfig(planningDir) {
|
|
@@ -111,9 +111,9 @@ function defaultConfig(domain) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
* Save a config object to `.planning/
|
|
114
|
+
* Save a config object to `.legion/planning/config.json`.
|
|
115
115
|
*
|
|
116
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
116
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
117
117
|
* @param {object} config - Config object to persist
|
|
118
118
|
*/
|
|
119
119
|
function saveConfig(planningDir, config) {
|
package/bin/lib/core.cjs
CHANGED
|
@@ -70,7 +70,7 @@ function currentTimestamp(format) {
|
|
|
70
70
|
* Walk up from `startDir` looking for a directory that matches `target`.
|
|
71
71
|
* Returns the absolute path to the found directory, or null.
|
|
72
72
|
*
|
|
73
|
-
* @param {string} target - Relative directory name to locate (e.g. '.planning
|
|
73
|
+
* @param {string} target - Relative directory name to locate (e.g. '.legion/planning')
|
|
74
74
|
* @param {string} [startDir=process.cwd()] - Where to start searching
|
|
75
75
|
* @returns {string|null}
|
|
76
76
|
*/
|
|
@@ -89,21 +89,30 @@ function _walkUp(target, startDir) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Find `.planning
|
|
92
|
+
* Find `.legion/planning/` by walking up from cwd.
|
|
93
93
|
* @param {string} [startDir]
|
|
94
94
|
* @returns {string|null}
|
|
95
95
|
*/
|
|
96
96
|
function resolvePlanningDir(startDir) {
|
|
97
|
-
return _walkUp(path.join('.
|
|
97
|
+
return _walkUp(path.join('.legion', 'planning'), startDir);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* Find `.codebase/` by walking up from cwd.
|
|
101
|
+
* Find `.legion/codebase/` by walking up from cwd.
|
|
102
102
|
* @param {string} [startDir]
|
|
103
103
|
* @returns {string|null}
|
|
104
104
|
*/
|
|
105
105
|
function resolveCodebaseDir(startDir) {
|
|
106
|
-
return _walkUp('.codebase', startDir);
|
|
106
|
+
return _walkUp(path.join('.legion', 'codebase'), startDir);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Find `.legion/` root by walking up from cwd.
|
|
111
|
+
* @param {string} [startDir]
|
|
112
|
+
* @returns {string|null}
|
|
113
|
+
*/
|
|
114
|
+
function resolveLegionDir(startDir) {
|
|
115
|
+
return _walkUp('.legion', startDir);
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
// ─── Task Numbering ──────────────────────────────────────────────────────────
|
|
@@ -112,7 +121,7 @@ function resolveCodebaseDir(startDir) {
|
|
|
112
121
|
* Scan existing artifact files in `planningDir/artifacts/` and return the next
|
|
113
122
|
* zero-padded three-digit number (e.g. "002").
|
|
114
123
|
*
|
|
115
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
124
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
116
125
|
* @returns {string} Next task number, zero-padded to 3 digits
|
|
117
126
|
*/
|
|
118
127
|
function nextTaskNum(planningDir) {
|
|
@@ -209,6 +218,7 @@ module.exports = {
|
|
|
209
218
|
currentTimestamp,
|
|
210
219
|
resolvePlanningDir,
|
|
211
220
|
resolveCodebaseDir,
|
|
221
|
+
resolveLegionDir,
|
|
212
222
|
nextTaskNum,
|
|
213
223
|
readJsonSafe,
|
|
214
224
|
writeJsonSafe,
|
package/bin/lib/init.cjs
CHANGED
|
@@ -5,9 +5,9 @@ const path = require('path');
|
|
|
5
5
|
const {
|
|
6
6
|
resolvePlanningDir,
|
|
7
7
|
resolveCodebaseDir,
|
|
8
|
+
resolveLegionDir,
|
|
8
9
|
currentTimestamp,
|
|
9
10
|
nextTaskNum,
|
|
10
|
-
banner,
|
|
11
11
|
} = require('./core.cjs');
|
|
12
12
|
const { loadConfig, defaultConfig, saveConfig } = require('./config.cjs');
|
|
13
13
|
const { loadState, initState } = require('./state.cjs');
|
|
@@ -59,9 +59,8 @@ function _scanDir(dir, base, depth, results) {
|
|
|
59
59
|
/**
|
|
60
60
|
* Initialize a workflow and return a full context JSON blob.
|
|
61
61
|
*
|
|
62
|
-
* - Detects `.codebase/` and its contents
|
|
63
|
-
* -
|
|
64
|
-
* - Ensures `.planning/legion/` exists
|
|
62
|
+
* - Detects `.legion/codebase/` and its contents
|
|
63
|
+
* - Ensures `.legion/planning/` exists
|
|
65
64
|
* - Loads or creates STATE.md
|
|
66
65
|
* - Loads or creates config.json
|
|
67
66
|
*
|
|
@@ -73,7 +72,20 @@ function initWorkflow(type, args) {
|
|
|
73
72
|
const domain = (type || 'devops').toLowerCase();
|
|
74
73
|
const cwd = process.cwd();
|
|
75
74
|
|
|
76
|
-
// ── Detect .
|
|
75
|
+
// ── Detect project root (parent of .legion/) ───────────────────────────
|
|
76
|
+
let projectRoot = cwd;
|
|
77
|
+
const legionDir = resolveLegionDir(cwd);
|
|
78
|
+
if (legionDir) {
|
|
79
|
+
projectRoot = path.dirname(legionDir);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Ensure .legion/ structure ──────────────────────────────────────────
|
|
83
|
+
const legionRoot = legionDir || path.join(projectRoot, '.legion');
|
|
84
|
+
if (!fs.existsSync(legionRoot)) {
|
|
85
|
+
fs.mkdirSync(legionRoot, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Detect .legion/codebase/ ───────────────────────────────────────────
|
|
77
89
|
const codebaseDir = resolveCodebaseDir(cwd);
|
|
78
90
|
const codebaseExists = codebaseDir !== null;
|
|
79
91
|
let codebaseFiles = [];
|
|
@@ -81,27 +93,12 @@ function initWorkflow(type, args) {
|
|
|
81
93
|
codebaseFiles = _scanDir(codebaseDir, codebaseDir);
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
// ──
|
|
85
|
-
let projectRoot = cwd;
|
|
86
|
-
if (codebaseDir) {
|
|
87
|
-
projectRoot = path.dirname(codebaseDir);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ── Detect .planning/codebase/ ─────────────────────────────────────────
|
|
91
|
-
const planningCodebaseDir = path.join(projectRoot, '.planning', 'codebase');
|
|
92
|
-
const planningCodebaseExists = fs.existsSync(planningCodebaseDir) &&
|
|
93
|
-
fs.statSync(planningCodebaseDir).isDirectory();
|
|
94
|
-
let planningCodebaseFiles = [];
|
|
95
|
-
if (planningCodebaseExists) {
|
|
96
|
-
planningCodebaseFiles = _scanDir(planningCodebaseDir, planningCodebaseDir);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ── Ensure .planning/legion/ ───────────────────────────────────────────
|
|
96
|
+
// ── Ensure .legion/planning/ ───────────────────────────────────────────
|
|
100
97
|
let planningDir = resolvePlanningDir(cwd);
|
|
101
98
|
const planningDirExisted = planningDir !== null;
|
|
102
99
|
|
|
103
100
|
if (!planningDir) {
|
|
104
|
-
planningDir = path.join(
|
|
101
|
+
planningDir = path.join(legionRoot, 'planning');
|
|
105
102
|
fs.mkdirSync(planningDir, { recursive: true });
|
|
106
103
|
}
|
|
107
104
|
|
|
@@ -147,11 +144,7 @@ function initWorkflow(type, args) {
|
|
|
147
144
|
files: codebaseFiles,
|
|
148
145
|
},
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
exists: planningCodebaseExists,
|
|
152
|
-
path: planningCodebaseExists ? planningCodebaseDir : null,
|
|
153
|
-
files: planningCodebaseFiles,
|
|
154
|
-
},
|
|
147
|
+
legionRoot: legionRoot,
|
|
155
148
|
|
|
156
149
|
planning: {
|
|
157
150
|
path: planningDir,
|
package/bin/lib/session.cjs
CHANGED
|
@@ -7,12 +7,12 @@ const { currentTimestamp } = require('./core.cjs');
|
|
|
7
7
|
// ─── Session Management ─────────────────────────────────────────────────────
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Create a new session record in `.planning/
|
|
10
|
+
* Create a new session record in `.legion/planning/sessions/`.
|
|
11
11
|
*
|
|
12
12
|
* Session file: `{YYYY-MM-DD}-{NNN}.md`
|
|
13
13
|
* NNN is a zero-padded counter within the same day.
|
|
14
14
|
*
|
|
15
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
15
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
16
16
|
* @param {object} data - Session data
|
|
17
17
|
* @param {string} [data.domain] - Domain name
|
|
18
18
|
* @param {string} [data.stage] - Pipeline stage
|
|
@@ -93,9 +93,9 @@ function createSession(planningDir, data) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
* Get the most recent session file from `.planning/
|
|
96
|
+
* Get the most recent session file from `.legion/planning/sessions/`.
|
|
97
97
|
*
|
|
98
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
98
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
99
99
|
* @returns {object|null} Session info: { path, name, content } or null
|
|
100
100
|
*/
|
|
101
101
|
function getLatestSession(planningDir) {
|
package/bin/lib/state.cjs
CHANGED
|
@@ -9,7 +9,7 @@ const { currentTimestamp } = require('./core.cjs');
|
|
|
9
9
|
/**
|
|
10
10
|
* Parse STATE.md into a structured object.
|
|
11
11
|
*
|
|
12
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
12
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
13
13
|
* @returns {object|null} Parsed state, or null if STATE.md doesn't exist
|
|
14
14
|
*/
|
|
15
15
|
function loadState(planningDir) {
|
|
@@ -101,7 +101,7 @@ function loadState(planningDir) {
|
|
|
101
101
|
/**
|
|
102
102
|
* Write a structured state object back to STATE.md.
|
|
103
103
|
*
|
|
104
|
-
* @param {string} planningDir - Absolute path to `.planning
|
|
104
|
+
* @param {string} planningDir - Absolute path to `.legion/planning/`
|
|
105
105
|
* @param {object} stateObj - State object (same shape as loadState output)
|
|
106
106
|
*/
|
|
107
107
|
function saveState(planningDir, stateObj) {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const PACKAGE_NAME = 'legion-cc';
|
|
9
|
+
const CACHE_TTL_SECONDS = 3600; // 1 hour
|
|
10
|
+
|
|
11
|
+
// ─── Version Utilities ──────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Read installed version from ~/.claude/legion/VERSION.
|
|
15
|
+
* Falls back to package.json, then '0.0.0'.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} [legionHome] - Path to ~/.claude/legion/
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function getInstalledVersion(legionHome) {
|
|
21
|
+
const home = legionHome || path.join(os.homedir(), '.claude', 'legion');
|
|
22
|
+
|
|
23
|
+
// Try VERSION file
|
|
24
|
+
const versionFile = path.join(home, 'VERSION');
|
|
25
|
+
try {
|
|
26
|
+
const v = fs.readFileSync(versionFile, 'utf8').trim();
|
|
27
|
+
if (v) return v;
|
|
28
|
+
} catch (_e) { /* fallthrough */ }
|
|
29
|
+
|
|
30
|
+
// Try package.json
|
|
31
|
+
const pkgFile = path.join(home, 'package.json');
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf8'));
|
|
34
|
+
if (pkg.version) return pkg.version;
|
|
35
|
+
} catch (_e) { /* fallthrough */ }
|
|
36
|
+
|
|
37
|
+
return '0.0.0';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compare two semver strings. Returns true if `latest` is newer than `current`.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} current
|
|
44
|
+
* @param {string} latest
|
|
45
|
+
* @returns {boolean}
|
|
46
|
+
*/
|
|
47
|
+
function isNewer(current, latest) {
|
|
48
|
+
const curr = current.split('.').map(Number);
|
|
49
|
+
const next = latest.split('.').map(Number);
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < 3; i++) {
|
|
52
|
+
const c = curr[i] || 0;
|
|
53
|
+
const n = next[i] || 0;
|
|
54
|
+
if (n > c) return true;
|
|
55
|
+
if (n < c) return false;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── npm Registry Query ─────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Query npm registry for the latest version of legion-cc.
|
|
64
|
+
* Uses `npm view` with a 5-second timeout.
|
|
65
|
+
*
|
|
66
|
+
* @returns {string|null} Latest version string, or null on failure
|
|
67
|
+
*/
|
|
68
|
+
function queryLatestVersion() {
|
|
69
|
+
try {
|
|
70
|
+
const result = spawnSync('npm', ['view', `${PACKAGE_NAME}@latest`, 'version'], {
|
|
71
|
+
timeout: 5000,
|
|
72
|
+
encoding: 'utf8',
|
|
73
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (result.status === 0 && result.stdout) {
|
|
77
|
+
return result.stdout.trim();
|
|
78
|
+
}
|
|
79
|
+
} catch (_e) { /* silent */ }
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ─── Cache ──────────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the cache file path.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} [legionHome]
|
|
90
|
+
* @returns {string}
|
|
91
|
+
*/
|
|
92
|
+
function cachePath(legionHome) {
|
|
93
|
+
const home = legionHome || path.join(os.homedir(), '.claude', 'legion');
|
|
94
|
+
return path.join(home, '.update-cache.json');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Read cached update check result. Returns null if stale or missing.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} [legionHome]
|
|
101
|
+
* @returns {object|null} { current, latest, available, checked_at }
|
|
102
|
+
*/
|
|
103
|
+
function readCache(legionHome) {
|
|
104
|
+
try {
|
|
105
|
+
const raw = fs.readFileSync(cachePath(legionHome), 'utf8');
|
|
106
|
+
const data = JSON.parse(raw);
|
|
107
|
+
|
|
108
|
+
const now = Math.floor(Date.now() / 1000);
|
|
109
|
+
if (data.checked_at && (now - data.checked_at) < CACHE_TTL_SECONDS) {
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
} catch (_e) { /* miss */ }
|
|
113
|
+
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Write update check result to cache.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} data
|
|
121
|
+
* @param {string} [legionHome]
|
|
122
|
+
*/
|
|
123
|
+
function writeCache(data, legionHome) {
|
|
124
|
+
try {
|
|
125
|
+
const p = cachePath(legionHome);
|
|
126
|
+
const dir = path.dirname(p);
|
|
127
|
+
if (!fs.existsSync(dir)) {
|
|
128
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
129
|
+
}
|
|
130
|
+
fs.writeFileSync(p, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
131
|
+
} catch (_e) { /* silent */ }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── Main Check ─────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if an update is available. Uses cache to avoid frequent npm queries.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} [legionHome] - Path to ~/.claude/legion/
|
|
140
|
+
* @returns {{ available: boolean, current: string, latest: string }|null}
|
|
141
|
+
*/
|
|
142
|
+
function checkForUpdate(legionHome) {
|
|
143
|
+
// Check cache first
|
|
144
|
+
const cached = readCache(legionHome);
|
|
145
|
+
if (cached) {
|
|
146
|
+
return {
|
|
147
|
+
available: cached.available,
|
|
148
|
+
current: cached.current,
|
|
149
|
+
latest: cached.latest,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Query npm
|
|
154
|
+
const current = getInstalledVersion(legionHome);
|
|
155
|
+
const latest = queryLatestVersion();
|
|
156
|
+
|
|
157
|
+
if (!latest) return null;
|
|
158
|
+
|
|
159
|
+
const available = isNewer(current, latest);
|
|
160
|
+
|
|
161
|
+
const result = {
|
|
162
|
+
available,
|
|
163
|
+
current,
|
|
164
|
+
latest,
|
|
165
|
+
checked_at: Math.floor(Date.now() / 1000),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
writeCache(result, legionHome);
|
|
169
|
+
|
|
170
|
+
return { available, current, latest };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── Exports ────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
getInstalledVersion,
|
|
177
|
+
isNewer,
|
|
178
|
+
queryLatestVersion,
|
|
179
|
+
checkForUpdate,
|
|
180
|
+
readCache,
|
|
181
|
+
writeCache,
|
|
182
|
+
CACHE_TTL_SECONDS,
|
|
183
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion:devops:architect
|
|
3
|
-
description: "Design infrastructure architecture with devops-architect agent — creates architecture documents in .planning/
|
|
3
|
+
description: "Design infrastructure architecture with devops-architect agent — creates architecture documents in .legion/planning/devops/"
|
|
4
4
|
argument-hint: "<architecture task description>"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|