@zhuk/spego 0.13.0 → 0.14.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/CHANGELOG.md +23 -2
- package/README.md +10 -11
- package/dist/cli/commands/board.d.ts +2 -0
- package/dist/cli/commands/{mirror.js → board.js} +10 -122
- package/dist/cli/commands/board.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts +3 -2
- package/dist/cli/commands/delivery.js +40 -34
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/sprints.d.ts +6 -0
- package/dist/cli/commands/sprints.js +35 -0
- package/dist/cli/commands/sprints.js.map +1 -0
- package/dist/cli/commands/view.d.ts +0 -3
- package/dist/cli/commands/view.js +1 -12
- package/dist/cli/commands/view.js.map +1 -1
- package/dist/cli/commands/workspace.js +50 -2
- package/dist/cli/commands/workspace.js.map +1 -1
- package/dist/cli/runtime.d.ts +0 -1
- package/dist/cli/runtime.js +1 -1
- package/dist/cli/runtime.js.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/command-meta/registry.d.ts +1 -0
- package/dist/command-meta/registry.js +18 -8
- package/dist/command-meta/registry.js.map +1 -1
- package/dist/delivery/load.d.ts +13 -0
- package/dist/delivery/load.js +124 -0
- package/dist/delivery/load.js.map +1 -0
- package/dist/delivery/mirror.d.ts +11 -0
- package/dist/delivery/mirror.js +14 -0
- package/dist/delivery/mirror.js.map +1 -1
- package/dist/delivery/openspec-adapter.d.ts +3 -1
- package/dist/delivery/openspec-adapter.js +34 -37
- package/dist/delivery/openspec-adapter.js.map +1 -1
- package/dist/delivery/openspec-discover.d.ts +0 -1
- package/dist/delivery/openspec-discover.js +13 -33
- package/dist/delivery/openspec-discover.js.map +1 -1
- package/dist/generator/claude.d.ts +1 -1
- package/dist/generator/claude.js +11 -4
- package/dist/generator/claude.js.map +1 -1
- package/dist/workflows/registry.js +5 -5
- package/dist/workflows/registry.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/mirror.d.ts +0 -2
- package/dist/cli/commands/mirror.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.14.0] - 2026-07-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `spego sprints` lists sprint plans in board order (start date, undated last).
|
|
9
|
+
- `spego status` reports advisory delivery drift (ungroomed changes, orphan epics, closable sprints) with a pointer to the groom workflow.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **Breaking:** renamed `spego mirror` to `spego board` (`/spego:board`); the delivery board name matches what the command renders. The Mirror stays the internal derivation concept.
|
|
13
|
+
- **Breaking:** `spego epics` and `spego tasks` take positional arguments: `spego epics [name]`, `spego tasks <change> [task]`. The `--change` and `--task` flags are gone; mutation verbs in the name position still fail with `DELIVERY_READ_ONLY`.
|
|
14
|
+
|
|
15
|
+
### Removed
|
|
16
|
+
- **Breaking:** removed the deprecated `spego view --format` flag; use the global `--json` flag.
|
|
17
|
+
|
|
18
|
+
## [0.13.1] - 2026-07-21
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- `spego mirror` / `spego epics` resolve epic status from `tasks.md` on disk instead of spawning `openspec status` per change (~114x faster on multi-change workspaces) and discover changes once per render with parallelized filesystem reads.
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
- Removed the per-change `openspec status` subprocess call and the "OpenSpec CLI status unavailable" fallback warning; mirror and epics no longer require the `openspec` binary on PATH.
|
|
25
|
+
|
|
3
26
|
## [0.13.0] - 2026-07-21
|
|
4
27
|
|
|
5
28
|
### Added
|
|
@@ -8,8 +31,6 @@
|
|
|
8
31
|
- `spego mirror` command deriving the delivery mirror (sprint board, blockers, gaps, drift warnings, next-change suggestion) from OpenSpec changes and `epic`/`sprint-plan` artifacts, with `--graph`, `--gaps`, and deterministic `--json` output.
|
|
9
32
|
- `spego-groom` workflow skill for syncing delivery-mirror epics with active OpenSpec changes, analyzing deps/requires/gaps, and maintaining sprint plans.
|
|
10
33
|
|
|
11
|
-
## [Unreleased]
|
|
12
|
-
|
|
13
34
|
## [0.12.0] - 2026-07-12
|
|
14
35
|
|
|
15
36
|
### Security
|
package/README.md
CHANGED
|
@@ -24,9 +24,10 @@ spego init --agents claude,opencode --demo false
|
|
|
24
24
|
| `spego commands` | List command metadata |
|
|
25
25
|
| `spego workflows` | List workflow skills metadata |
|
|
26
26
|
| `spego skills` | Regenerate agent skill files |
|
|
27
|
-
| `spego epics` | List delivery epics |
|
|
28
|
-
| `spego tasks
|
|
29
|
-
| `spego
|
|
27
|
+
| `spego epics [name]` | List delivery epics, or get one by change name |
|
|
28
|
+
| `spego tasks <change> [task]` | List tasks for a change, or get one task |
|
|
29
|
+
| `spego sprints` | List sprint plans in board order |
|
|
30
|
+
| `spego board` | Show the delivery board (sprints, blockers, gaps) |
|
|
30
31
|
| `spego index rebuild` | Rebuild SQLite index from files |
|
|
31
32
|
|
|
32
33
|
All commands accept `--cwd <dir>` to set the project root.
|
|
@@ -40,8 +41,6 @@ Every spego command speaks two output modes:
|
|
|
40
41
|
|
|
41
42
|
Agents calling spego MUST pass `--json` so they get parseable output. Humans normally do not.
|
|
42
43
|
|
|
43
|
-
The legacy `spego view --format markdown|json` flag is **deprecated** and will be removed in the next minor release. Use the global `--json` flag instead. The old flag still works during the deprecation window and prints a single `deprecated:` warning line to stderr.
|
|
44
|
-
|
|
45
44
|
`spego read --json` and `spego update --json` both return artifact records with `frontmatter`, `body`, and `path`; `update` also keeps the compatibility fields `id` and `revision`.
|
|
46
45
|
|
|
47
46
|
`spego create` and `spego update` accept body content through `--body`, `--body-file <path>`, or stdin via `--body-file -`.
|
|
@@ -89,19 +88,19 @@ The adapter is read-only. It observes OpenSpec state but does not create, contin
|
|
|
89
88
|
| Source | spego view |
|
|
90
89
|
|--------|------------|
|
|
91
90
|
| `openspec/changes/<name>/proposal.md` | Epic title |
|
|
92
|
-
| `openspec/changes/<name>/tasks.md` | Task
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
| `openspec/changes/<name>/tasks.md` | Task status and counts |
|
|
92
|
+
|
|
93
|
+
The `openspec` binary is not required on PATH — board and epics resolve status entirely from `tasks.md` on disk.
|
|
95
94
|
|
|
96
95
|
Archived changes under `openspec/changes/archive/` are excluded. A change with no `tasks.md` is reported as `planning-incomplete`.
|
|
97
96
|
|
|
98
97
|
## Delivery Mirror
|
|
99
98
|
|
|
100
|
-
`spego
|
|
99
|
+
`spego board` derives the delivery mirror on demand — it never writes artifacts or OpenSpec state. It combines active and archived OpenSpec changes with `epic` and `sprint-plan` artifacts into a sprint board: sprints in date order (undated last), changes in each sprint-plan's list order, per-change status, blockers, gaps, and missing artifacts (`requires` minus resolvable `links`). A change is blocked when a dependency is incomplete and not scheduled in the same or an earlier sprint.
|
|
101
100
|
|
|
102
101
|
The default output is the human board. `--graph` shows dependency edges; `--gaps` focuses on gap flags and missing artifacts. The global `--json` flag emits a deterministic `{ sprints, ungrouped, warnings, next }` document in all modes; `next` names the first pending, unblocked change or is `null` with a hint to groom.
|
|
103
102
|
|
|
104
|
-
Every rendering attaches drift warnings: `dangling-dep`, `dep-cycle`, `ungroomed-change`, `orphan-epic`, `archived-in-sprint`, and `closable-sprint`. Repair belongs to the groom workflow;
|
|
103
|
+
Every rendering attaches drift warnings: `dangling-dep`, `dep-cycle`, `ungroomed-change`, `orphan-epic`, `archived-in-sprint`, and `closable-sprint`. Repair belongs to the groom workflow; the board only reports.
|
|
105
104
|
|
|
106
105
|
## OpenCode Workflows
|
|
107
106
|
|
|
@@ -229,7 +228,7 @@ If a combined workflow discovers that OpenSpec artifacts need to change, use the
|
|
|
229
228
|
|
|
230
229
|
Delivery-mirror workflow that reconciles active OpenSpec changes with `epic` and `sprint-plan` artifacts. One Groomer persona runs five phases:
|
|
231
230
|
|
|
232
|
-
1. `orient` — read `spego
|
|
231
|
+
1. `orient` — read `spego board --json` and `spego epics --json`, then classify drift warnings.
|
|
233
232
|
2. `sync` — create missing epics for ungroomed active changes and propose orphan-epic disposition.
|
|
234
233
|
3. `analyze` — update deps, requires, supporting links, and gap notes on epics.
|
|
235
234
|
4. `plan` — propose releasable, testable sprint groupings and create or update `sprint-plan` artifacts after confirmation.
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { discoverChanges } from '../../delivery/openspec-discover.js';
|
|
4
|
-
import { deriveMirror, filterMirrorGaps, } from '../../delivery/mirror.js';
|
|
5
|
-
import { SpegoError } from '../../errors.js';
|
|
6
|
-
import { readConfig } from '../../workspace/config.js';
|
|
7
|
-
import { resolveWorkspacePaths } from '../../workspace/paths.js';
|
|
1
|
+
import { loadBoardState } from '../../delivery/load.js';
|
|
2
|
+
import { filterMirrorGaps, } from '../../delivery/mirror.js';
|
|
8
3
|
import { renderHeader, renderTable } from '../render.js';
|
|
9
4
|
import { runEngineCommand } from '../runtime.js';
|
|
10
|
-
export function
|
|
5
|
+
export function registerBoard(program) {
|
|
11
6
|
program
|
|
12
|
-
.command('
|
|
13
|
-
.description('Show delivery
|
|
7
|
+
.command('board')
|
|
8
|
+
.description('Show the delivery board (sprints, blockers, gaps)')
|
|
14
9
|
.option('--graph', 'show dependency graph', false)
|
|
15
10
|
.option('--gaps', 'show gaps, missing artifacts, and blockers', false)
|
|
16
11
|
.option('--cwd <dir>', 'project root')
|
|
17
12
|
.action(async (opts) => {
|
|
18
13
|
await runEngineCommand({ program, cwd: opts.cwd }, async (engine) => {
|
|
19
|
-
const state = await
|
|
14
|
+
const state = await loadBoardState(engine, opts.cwd);
|
|
20
15
|
const payload = opts.gaps ? filterMirrorGaps(state.board) : state.board;
|
|
21
16
|
return {
|
|
22
17
|
payload,
|
|
@@ -31,115 +26,8 @@ export function registerMirror(program) {
|
|
|
31
26
|
});
|
|
32
27
|
});
|
|
33
28
|
}
|
|
34
|
-
async function loadMirrorState(engine, cwd) {
|
|
35
|
-
const projectRoot = path.resolve(cwd ?? process.cwd());
|
|
36
|
-
const wsPaths = resolveWorkspacePaths(projectRoot);
|
|
37
|
-
const config = await readConfig(wsPaths.configPath);
|
|
38
|
-
const adapter = resolveAdapter(projectRoot, config);
|
|
39
|
-
try {
|
|
40
|
-
const input = await collectMirrorInput(engine, projectRoot, adapter);
|
|
41
|
-
return { input, board: deriveMirror(input) };
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
if (err instanceof SpegoError && err.code === 'DELIVERY_ADAPTER_ERROR') {
|
|
45
|
-
const warning = {
|
|
46
|
-
code: 'adapter-unavailable',
|
|
47
|
-
message: 'OpenSpec workspace unavailable; mirror board is empty.',
|
|
48
|
-
details: err.details,
|
|
49
|
-
};
|
|
50
|
-
const input = {
|
|
51
|
-
changes: [],
|
|
52
|
-
epics: [],
|
|
53
|
-
sprints: [],
|
|
54
|
-
linkedArtifacts: [],
|
|
55
|
-
warnings: [warning],
|
|
56
|
-
};
|
|
57
|
-
return { input, board: deriveMirror(input) };
|
|
58
|
-
}
|
|
59
|
-
throw err;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async function collectMirrorInput(engine, projectRoot, adapter) {
|
|
63
|
-
const adapterEpics = await adapter.listEpics();
|
|
64
|
-
const discovered = await discoverChanges(projectRoot);
|
|
65
|
-
const adapterBySlug = new Map(adapterEpics.map((epic) => [epic.externalId, epic]));
|
|
66
|
-
const seen = new Set();
|
|
67
|
-
const changes = discovered
|
|
68
|
-
.map((item) => {
|
|
69
|
-
seen.add(item.name);
|
|
70
|
-
const adapterEpic = adapterBySlug.get(item.name);
|
|
71
|
-
return sourceChange(item.name, item.archived, adapterEpic);
|
|
72
|
-
})
|
|
73
|
-
.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
74
|
-
for (const epic of adapterEpics) {
|
|
75
|
-
if (seen.has(epic.externalId))
|
|
76
|
-
continue;
|
|
77
|
-
changes.push(sourceChange(epic.externalId, false, epic));
|
|
78
|
-
}
|
|
79
|
-
changes.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
80
|
-
const epics = engine.list({ type: 'epic' }).map(indexedArtifact);
|
|
81
|
-
const sprints = engine.list({ type: 'sprint-plan' }).map(indexedArtifact);
|
|
82
|
-
const linkedArtifacts = await resolveLinkedArtifacts(engine, epics);
|
|
83
|
-
return { changes, epics, sprints, linkedArtifacts, warnings: [] };
|
|
84
|
-
}
|
|
85
|
-
function sourceChange(slug, archived, epic) {
|
|
86
|
-
const status = archived ? 'completed' : (epic?.status ?? 'unknown');
|
|
87
|
-
return {
|
|
88
|
-
slug,
|
|
89
|
-
title: epic?.title ?? slug,
|
|
90
|
-
status,
|
|
91
|
-
archived,
|
|
92
|
-
warnings: epic?.warnings,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function indexedArtifact(artifact) {
|
|
96
|
-
return {
|
|
97
|
-
id: artifact.id,
|
|
98
|
-
type: artifact.type,
|
|
99
|
-
slug: artifact.slug,
|
|
100
|
-
title: artifact.title,
|
|
101
|
-
meta: artifact.meta,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
async function resolveLinkedArtifacts(engine, epics) {
|
|
105
|
-
const ids = new Set();
|
|
106
|
-
for (const epic of epics) {
|
|
107
|
-
const links = epic.meta.links;
|
|
108
|
-
if (!Array.isArray(links))
|
|
109
|
-
continue;
|
|
110
|
-
for (const link of links) {
|
|
111
|
-
if (typeof link === 'string')
|
|
112
|
-
ids.add(link);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const artifacts = [];
|
|
116
|
-
for (const id of [...ids].sort()) {
|
|
117
|
-
try {
|
|
118
|
-
const record = await engine.readById(id);
|
|
119
|
-
artifacts.push({
|
|
120
|
-
id: record.frontmatter.id,
|
|
121
|
-
type: record.frontmatter.type,
|
|
122
|
-
slug: record.frontmatter.slug,
|
|
123
|
-
title: record.frontmatter.title,
|
|
124
|
-
meta: record.frontmatter.meta,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
if (err instanceof SpegoError && err.code === 'ARTIFACT_NOT_FOUND')
|
|
129
|
-
continue;
|
|
130
|
-
throw err;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
artifacts.sort((a, b) => {
|
|
134
|
-
const bySlug = a.slug.localeCompare(b.slug);
|
|
135
|
-
if (bySlug !== 0)
|
|
136
|
-
return bySlug;
|
|
137
|
-
return a.id.localeCompare(b.id);
|
|
138
|
-
});
|
|
139
|
-
return artifacts;
|
|
140
|
-
}
|
|
141
29
|
function renderBoard(board) {
|
|
142
|
-
const lines = [renderHeader('🪞', '
|
|
30
|
+
const lines = [renderHeader('🪞', 'Delivery board'), ''];
|
|
143
31
|
if (board.sprints.length === 0 && board.ungrouped.length === 0) {
|
|
144
32
|
lines.push('No groomed delivery board.');
|
|
145
33
|
}
|
|
@@ -171,7 +59,7 @@ function renderGraph(board, input) {
|
|
|
171
59
|
rows.push([change.slug, visible.has(dep) ? dep : `${dep} (missing)`, change.blockers.join(', ') || '—', change.status]);
|
|
172
60
|
}
|
|
173
61
|
}
|
|
174
|
-
const lines = [renderHeader('🕸️', '
|
|
62
|
+
const lines = [renderHeader('🕸️', 'Dependency graph'), ''];
|
|
175
63
|
if (rows.length === 0)
|
|
176
64
|
lines.push('No dependency edges.');
|
|
177
65
|
else
|
|
@@ -191,7 +79,7 @@ function renderGaps(board) {
|
|
|
191
79
|
for (const change of board.ungrouped) {
|
|
192
80
|
rows.push([change.slug, '—', change.blockers.join(', ') || '—', formatGaps(change), change.missing.join(', ') || '—']);
|
|
193
81
|
}
|
|
194
|
-
const lines = [renderHeader('🧩', '
|
|
82
|
+
const lines = [renderHeader('🧩', 'Delivery gaps'), ''];
|
|
195
83
|
if (rows.length === 0)
|
|
196
84
|
lines.push('No gaps, missing artifacts, or blockers.');
|
|
197
85
|
else
|
|
@@ -250,4 +138,4 @@ function allChanges(board) {
|
|
|
250
138
|
changes.push(...board.ungrouped);
|
|
251
139
|
return changes;
|
|
252
140
|
}
|
|
253
|
-
//# sourceMappingURL=
|
|
141
|
+
//# sourceMappingURL=board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.js","sourceRoot":"","sources":["../../../src/cli/commands/board.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Delivery commands: read-only `epics` and `tasks` views over the configured
|
|
3
|
-
* delivery adapter (default: OpenSpec).
|
|
4
|
-
* `DELIVERY_READ_ONLY` so
|
|
3
|
+
* delivery adapter (default: OpenSpec). Mutation verbs in the name position
|
|
4
|
+
* are rejected with `DELIVERY_READ_ONLY` so agents get a structured pointer
|
|
5
|
+
* to the right tool.
|
|
5
6
|
*/
|
|
6
7
|
import type { Command } from 'commander';
|
|
7
8
|
export declare function registerDelivery(program: Command): void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Delivery commands: read-only `epics` and `tasks` views over the configured
|
|
3
|
-
* delivery adapter (default: OpenSpec).
|
|
4
|
-
* `DELIVERY_READ_ONLY` so
|
|
3
|
+
* delivery adapter (default: OpenSpec). Mutation verbs in the name position
|
|
4
|
+
* are rejected with `DELIVERY_READ_ONLY` so agents get a structured pointer
|
|
5
|
+
* to the right tool.
|
|
5
6
|
*/
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import { resolveWorkspacePaths } from '../../workspace/paths.js';
|
|
@@ -10,11 +11,24 @@ import { resolveAdapter } from '../../delivery/index.js';
|
|
|
10
11
|
import { SpegoError } from '../../errors.js';
|
|
11
12
|
import { renderHeader, renderTable } from '../render.js';
|
|
12
13
|
import { runCommand } from '../runtime.js';
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const MUTATING_ACTIONS = new Set([
|
|
15
|
+
'create', 'update', 'delete', 'add', 'remove', 'edit', 'set', 'complete', 'close', 'archive',
|
|
16
|
+
]);
|
|
17
|
+
function assertReadOnly(name, surface, usage) {
|
|
18
|
+
if (!name)
|
|
16
19
|
return;
|
|
17
|
-
|
|
20
|
+
if (MUTATING_ACTIONS.has(name)) {
|
|
21
|
+
throw new SpegoError('DELIVERY_READ_ONLY', `Cannot '${name}' delivery ${surface} through spego. Use the OpenSpec CLI for delivery mutations.`, { attemptedAction: name, suggestedTool: 'openspec' });
|
|
22
|
+
}
|
|
23
|
+
if (name === 'get') {
|
|
24
|
+
throw new SpegoError('VALIDATION_FAILED', `'get' is no longer an action; usage: ${usage}`, { usage });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function loadAdapter(cwd) {
|
|
28
|
+
const projectRoot = path.resolve(cwd ?? process.cwd());
|
|
29
|
+
const wsPaths = resolveWorkspacePaths(projectRoot);
|
|
30
|
+
const config = await readConfig(wsPaths.configPath);
|
|
31
|
+
return resolveAdapter(projectRoot, config);
|
|
18
32
|
}
|
|
19
33
|
function renderEpicRow(epic) {
|
|
20
34
|
const progress = epic.taskCount != null ? `${epic.tasksDone ?? 0}/${epic.taskCount}` : '—';
|
|
@@ -26,19 +40,15 @@ function renderTaskRow(task) {
|
|
|
26
40
|
export function registerDelivery(program) {
|
|
27
41
|
program
|
|
28
42
|
.command('epics')
|
|
29
|
-
.description('List epics or get a single epic')
|
|
30
|
-
.argument('[
|
|
31
|
-
.option('--change <name>', 'get a single epic by name')
|
|
43
|
+
.description('List epics or get a single epic by change name')
|
|
44
|
+
.argument('[name]', 'change name to get a single epic')
|
|
32
45
|
.option('--cwd <dir>', 'project root')
|
|
33
|
-
.action(async (
|
|
46
|
+
.action(async (name, opts) => {
|
|
34
47
|
await runCommand({ program }, async () => {
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const adapter = resolveAdapter(projectRoot, config);
|
|
40
|
-
if (opts.change) {
|
|
41
|
-
const epic = await adapter.getEpic(opts.change);
|
|
48
|
+
assertReadOnly(name, 'epics', 'spego epics <name>');
|
|
49
|
+
const adapter = await loadAdapter(opts.cwd);
|
|
50
|
+
if (name && name !== 'list') {
|
|
51
|
+
const epic = await adapter.getEpic(name);
|
|
42
52
|
return {
|
|
43
53
|
payload: epic,
|
|
44
54
|
human: () => {
|
|
@@ -62,35 +72,31 @@ export function registerDelivery(program) {
|
|
|
62
72
|
program
|
|
63
73
|
.command('tasks')
|
|
64
74
|
.description('List tasks for a change or get a single task')
|
|
65
|
-
.argument('
|
|
66
|
-
.
|
|
67
|
-
.option('--task <id>', 'get a single task by id')
|
|
75
|
+
.argument('<change>', 'change or epic name')
|
|
76
|
+
.argument('[task]', 'task id to get a single task')
|
|
68
77
|
.option('--cwd <dir>', 'project root')
|
|
69
|
-
.action(async (
|
|
78
|
+
.action(async (change, task, opts) => {
|
|
70
79
|
await runCommand({ program }, async () => {
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const adapter = resolveAdapter(projectRoot, config);
|
|
76
|
-
if (opts.task) {
|
|
77
|
-
const task = await adapter.getTask(opts.change, opts.task);
|
|
80
|
+
assertReadOnly(change, 'tasks', 'spego tasks <change> [task]');
|
|
81
|
+
const adapter = await loadAdapter(opts.cwd);
|
|
82
|
+
if (task) {
|
|
83
|
+
const found = await adapter.getTask(change, task);
|
|
78
84
|
return {
|
|
79
|
-
payload:
|
|
85
|
+
payload: found,
|
|
80
86
|
human: () => {
|
|
81
|
-
const table = renderTable(['done', 'title'], [renderTaskRow(
|
|
82
|
-
return `${renderHeader('✅', `Task: ${
|
|
87
|
+
const table = renderTable(['done', 'title'], [renderTaskRow(found)]);
|
|
88
|
+
return `${renderHeader('✅', `Task: ${change} (${adapter.name})`)}\n${table}`;
|
|
83
89
|
},
|
|
84
90
|
};
|
|
85
91
|
}
|
|
86
|
-
const tasks = await adapter.listTasks(
|
|
92
|
+
const tasks = await adapter.listTasks(change);
|
|
87
93
|
return {
|
|
88
94
|
payload: tasks,
|
|
89
95
|
human: () => {
|
|
90
96
|
if (tasks.length === 0)
|
|
91
|
-
return `No tasks for ${
|
|
97
|
+
return `No tasks for ${change}.`;
|
|
92
98
|
const table = renderTable(['done', 'title'], tasks.map(renderTaskRow));
|
|
93
|
-
return `${renderHeader('✅', `Tasks: ${
|
|
99
|
+
return `${renderHeader('✅', `Tasks: ${change} (${adapter.name})`)}\n${table}`;
|
|
94
100
|
},
|
|
95
101
|
};
|
|
96
102
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../../src/cli/commands/delivery.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../../src/cli/commands/delivery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint listing: `spego sprints` — a read-only view over `sprint-plan`
|
|
3
|
+
* artifacts in board order. Membership stays owned by the groom workflow.
|
|
4
|
+
*/
|
|
5
|
+
import { toMirrorArtifact } from '../../delivery/load.js';
|
|
6
|
+
import { summarizeSprints } from '../../delivery/mirror.js';
|
|
7
|
+
import { renderHeader, renderTable } from '../render.js';
|
|
8
|
+
import { runEngineCommand } from '../runtime.js';
|
|
9
|
+
function sprintRow(sprint) {
|
|
10
|
+
const window = sprint.startDate || sprint.endDate
|
|
11
|
+
? `${sprint.startDate ?? '…'} → ${sprint.endDate ?? '…'}`
|
|
12
|
+
: '—';
|
|
13
|
+
return [sprint.slug, sprint.status, window, String(sprint.changes.length), sprint.title];
|
|
14
|
+
}
|
|
15
|
+
export function registerSprints(program) {
|
|
16
|
+
program
|
|
17
|
+
.command('sprints')
|
|
18
|
+
.description('List sprint plans in board order')
|
|
19
|
+
.option('--cwd <dir>', 'project root')
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
await runEngineCommand({ program, cwd: opts.cwd }, async (engine) => {
|
|
22
|
+
const sprints = summarizeSprints(engine.list({ type: 'sprint-plan' }).map(toMirrorArtifact));
|
|
23
|
+
return {
|
|
24
|
+
payload: sprints,
|
|
25
|
+
human: () => {
|
|
26
|
+
if (sprints.length === 0)
|
|
27
|
+
return 'No sprint plans.';
|
|
28
|
+
const table = renderTable(['sprint', 'status', 'window', 'changes', 'title'], sprints.map(sprintRow), { maxWidth: 48 });
|
|
29
|
+
return `${renderHeader('🏃', 'Sprints')}\n${table}`;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=sprints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sprints.js","sourceRoot":"","sources":["../../../src/cli/commands/sprints.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAsB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,SAAS,SAAS,CAAC,MAAqB;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO;QAC/C,CAAC,CAAC,GAAG,MAAM,CAAC,SAAS,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE;QACzD,CAAC,CAAC,GAAG,CAAC;IACR,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC;SACrC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,gBAAgB,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAClE,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAC7F,OAAO;gBACL,OAAO,EAAE,OAAO;gBAChB,KAAK,EAAE,GAAG,EAAE;oBACV,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;wBAAE,OAAO,kBAAkB,CAAC;oBACpD,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;oBACxH,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC;gBACtD,CAAC;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bundle export: `spego view`.
|
|
3
|
-
*
|
|
4
|
-
* Honors the deprecated `--format` flag during the deprecation window in
|
|
5
|
-
* addition to the global `--json` flag.
|
|
6
3
|
*/
|
|
7
4
|
import type { Command } from 'commander';
|
|
8
5
|
export declare function registerView(program: Command): void;
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bundle export: `spego view`.
|
|
3
|
-
*
|
|
4
|
-
* Honors the deprecated `--format` flag during the deprecation window in
|
|
5
|
-
* addition to the global `--json` flag.
|
|
6
3
|
*/
|
|
7
4
|
import { z } from 'zod';
|
|
8
5
|
import { SpegoError } from '../../errors.js';
|
|
9
6
|
import { viewArtifacts } from '../../export/view.js';
|
|
10
7
|
import { intersperseBundleDividers, renderHeader } from '../render.js';
|
|
11
|
-
import {
|
|
12
|
-
import { getJsonMode, runEngineCommand } from '../runtime.js';
|
|
8
|
+
import { runEngineCommand } from '../runtime.js';
|
|
13
9
|
export function registerView(program) {
|
|
14
10
|
program
|
|
15
11
|
.command('view')
|
|
@@ -18,19 +14,12 @@ export function registerView(program) {
|
|
|
18
14
|
.option('--id <id>', 'limit to a single artifact')
|
|
19
15
|
.option('--revision <n>', 'specific revision (requires --id)')
|
|
20
16
|
.option('--include-deleted', 'include soft-deleted artifacts', false)
|
|
21
|
-
.option('--format <fmt>', '[deprecated] markdown | json — use the global --json flag instead')
|
|
22
17
|
.option('--cwd <dir>', 'project root')
|
|
23
18
|
.action(async (opts) => {
|
|
24
|
-
const globalJson = getJsonMode(program);
|
|
25
|
-
if (opts.format !== undefined) {
|
|
26
|
-
deprecate(globalJson, '--format is deprecated; use the global --json flag');
|
|
27
|
-
}
|
|
28
|
-
const wantJson = globalJson || opts.format === 'json';
|
|
29
19
|
let revision;
|
|
30
20
|
await runEngineCommand({
|
|
31
21
|
program,
|
|
32
22
|
cwd: opts.cwd,
|
|
33
|
-
jsonOverride: wantJson,
|
|
34
23
|
validate: () => {
|
|
35
24
|
if (opts.revision === undefined)
|
|
36
25
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/cli/commands/view.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/cli/commands/view.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC5C,OAAO;SACL,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,iEAAiE,CAAC;SAC9E,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAAC;SACzC,MAAM,CAAC,WAAW,EAAE,4BAA4B,CAAC;SACjD,MAAM,CAAC,gBAAgB,EAAE,mCAAmC,CAAC;SAC7D,MAAM,CAAC,mBAAmB,EAAE,gCAAgC,EAAE,KAAK,CAAC;SACpE,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC;SACrC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,IAAI,QAA4B,CAAC;QACjC,MAAM,gBAAgB,CACrB;YACC,OAAO;YACP,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,QAAQ,EAAE,GAAG,EAAE;gBACd,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;oBAAE,OAAO;gBACxC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC3E,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC;oBACvB,OAAO;gBACR,CAAC;gBACD,MAAM,IAAI,UAAU,CAAC,mBAAmB,EAAE,iDAAiD,EAAE;oBAC5F,MAAM,EAAE,YAAY;iBACpB,CAAC,CAAC;YACJ,CAAC;SACD,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;YAChB,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE;gBACxC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,QAAQ;gBACR,cAAc,EAAE,IAAI,CAAC,cAAc;aACnC,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC9D,OAAO;gBACN,OAAO,EAAE,IAAI,CAAC,IAAI;gBAClB,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,iBAAiB,CAAC,KAAK,IAAI,EAAE;aAChE,CAAC;QACH,CAAC,CACD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workspace lifecycle commands: `init` and `status`.
|
|
3
3
|
*/
|
|
4
|
+
import { ArtifactEngine } from '../../artifacts/engine.js';
|
|
5
|
+
import { loadBoardState } from '../../delivery/load.js';
|
|
4
6
|
import { initWorkspace, workspaceStatus } from '../../workspace/init.js';
|
|
5
7
|
import { renderBox, renderHeader } from '../render.js';
|
|
6
8
|
import { runCommand } from '../runtime.js';
|
|
9
|
+
const DRIFT_CODES = new Set([
|
|
10
|
+
'dangling-dep', 'dep-cycle', 'ungroomed-change', 'orphan-epic', 'archived-in-sprint', 'closable-sprint',
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* Drift is advisory: any failure to derive the board (no OpenSpec workspace,
|
|
14
|
+
* broken adapter) silently yields no summary so `status` never breaks.
|
|
15
|
+
*/
|
|
16
|
+
async function collectDrift(cwd) {
|
|
17
|
+
let engine;
|
|
18
|
+
try {
|
|
19
|
+
engine = await ArtifactEngine.open({ projectRoot: cwd });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const { board } = await loadBoardState(engine, cwd);
|
|
26
|
+
if (board.warnings.some((warning) => warning.code === 'adapter-unavailable'))
|
|
27
|
+
return undefined;
|
|
28
|
+
const codes = {};
|
|
29
|
+
let warnings = 0;
|
|
30
|
+
for (const warning of board.warnings) {
|
|
31
|
+
if (!DRIFT_CODES.has(warning.code))
|
|
32
|
+
continue;
|
|
33
|
+
warnings += 1;
|
|
34
|
+
codes[warning.code] = (codes[warning.code] ?? 0) + 1;
|
|
35
|
+
}
|
|
36
|
+
return { warnings, codes };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
engine.close();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function driftLine(drift) {
|
|
46
|
+
const parts = Object.entries(drift.codes)
|
|
47
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
48
|
+
.map(([code, count]) => (count > 1 ? `${code} ×${count}` : code));
|
|
49
|
+
return `Delivery drift: ${parts.join(', ')} — run the spego-groom workflow.`;
|
|
50
|
+
}
|
|
7
51
|
export function registerWorkspace(program) {
|
|
8
52
|
program
|
|
9
53
|
.command('init')
|
|
@@ -43,6 +87,7 @@ export function registerWorkspace(program) {
|
|
|
43
87
|
.action(async (opts) => {
|
|
44
88
|
await runCommand({ program }, async () => {
|
|
45
89
|
const status = await workspaceStatus(opts.cwd);
|
|
90
|
+
const drift = status.initialized ? await collectDrift(opts.cwd) : undefined;
|
|
46
91
|
const human = () => {
|
|
47
92
|
if (!status.initialized)
|
|
48
93
|
return `No spego workspace; run: spego init`;
|
|
@@ -52,9 +97,12 @@ export function registerWorkspace(program) {
|
|
|
52
97
|
['artifacts', status.artifactsRoot ?? ''],
|
|
53
98
|
['index', status.indexPath ?? ''],
|
|
54
99
|
]);
|
|
55
|
-
|
|
100
|
+
const lines = [`${renderHeader('📦', 'Workspace ready')}\n${box}`];
|
|
101
|
+
if (drift && drift.warnings > 0)
|
|
102
|
+
lines.push(driftLine(drift));
|
|
103
|
+
return lines.join('\n');
|
|
56
104
|
};
|
|
57
|
-
return { payload: status, human };
|
|
105
|
+
return { payload: drift === undefined ? status : { ...status, drift }, human };
|
|
58
106
|
});
|
|
59
107
|
});
|
|
60
108
|
}
|