@lumenflow/cli 2.10.0 → 2.12.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 +28 -18
- package/dist/__tests__/commands.test.js +198 -2
- package/dist/__tests__/gates-integration-tests.test.js +112 -0
- package/dist/__tests__/init-docs-structure.test.js +33 -0
- package/dist/__tests__/init.test.js +225 -0
- package/dist/__tests__/initiative-add-wu.test.js +71 -1
- package/dist/__tests__/no-beacon-references-docs.test.js +30 -0
- package/dist/__tests__/no-beacon-references.test.js +39 -0
- package/dist/__tests__/safe-git.test.js +4 -4
- package/dist/__tests__/wu-create-required-fields.test.js +22 -0
- package/dist/__tests__/wu-create.test.js +121 -0
- package/dist/__tests__/wu-done-docs-only-policy.test.js +20 -0
- package/dist/__tests__/wu-prep-default-exec.test.js +35 -0
- package/dist/__tests__/wu-prep.test.js +32 -0
- package/dist/__tests__/wu-validate.test.js +36 -0
- package/dist/agent-issues-query.js +1 -0
- package/dist/agent-issues-query.js.map +1 -0
- package/dist/agent-log-issue.js +1 -0
- package/dist/agent-log-issue.js.map +1 -0
- package/dist/agent-session-end.js +1 -0
- package/dist/agent-session-end.js.map +1 -0
- package/dist/agent-session.js +1 -0
- package/dist/agent-session.js.map +1 -0
- package/dist/backlog-prune.js +2 -0
- package/dist/backlog-prune.js.map +1 -0
- package/dist/cli-entry-point.js +1 -0
- package/dist/cli-entry-point.js.map +1 -0
- package/dist/commands/integrate.js +1 -0
- package/dist/commands/integrate.js.map +1 -0
- package/dist/commands.js +56 -77
- package/dist/commands.js.map +1 -0
- package/dist/deps-add.js +1 -0
- package/dist/deps-add.js.map +1 -0
- package/dist/deps-remove.js +1 -0
- package/dist/deps-remove.js.map +1 -0
- package/dist/docs-sync.js +1 -0
- package/dist/docs-sync.js.map +1 -0
- package/dist/doctor.js +1 -0
- package/dist/doctor.js.map +1 -0
- package/dist/file-delete.js +1 -0
- package/dist/file-delete.js.map +1 -0
- package/dist/file-edit.js +1 -0
- package/dist/file-edit.js.map +1 -0
- package/dist/file-read.js +1 -0
- package/dist/file-read.js.map +1 -0
- package/dist/file-write.js +1 -0
- package/dist/file-write.js.map +1 -0
- package/dist/flow-bottlenecks.js +1 -0
- package/dist/flow-bottlenecks.js.map +1 -0
- package/dist/flow-report.js +1 -0
- package/dist/flow-report.js.map +1 -0
- package/dist/gates.js +32 -20
- package/dist/gates.js.map +1 -0
- package/dist/git-branch.js +1 -0
- package/dist/git-branch.js.map +1 -0
- package/dist/git-diff.js +1 -0
- package/dist/git-diff.js.map +1 -0
- package/dist/git-log.js +1 -0
- package/dist/git-log.js.map +1 -0
- package/dist/git-status.js +1 -0
- package/dist/git-status.js.map +1 -0
- package/dist/guard-locked.js +1 -0
- package/dist/guard-locked.js.map +1 -0
- package/dist/guard-main-branch.js +1 -0
- package/dist/guard-main-branch.js.map +1 -0
- package/dist/guard-worktree-commit.js +1 -0
- package/dist/guard-worktree-commit.js.map +1 -0
- package/dist/hooks/auto-checkpoint-utils.js +52 -0
- package/dist/hooks/auto-checkpoint-utils.js.map +1 -0
- package/dist/hooks/enforcement-checks.js +1 -0
- package/dist/hooks/enforcement-checks.js.map +1 -0
- package/dist/hooks/enforcement-generator.js +185 -1
- package/dist/hooks/enforcement-generator.js.map +1 -0
- package/dist/hooks/enforcement-sync.js +91 -1
- package/dist/hooks/enforcement-sync.js.map +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/init.js +176 -36
- package/dist/init.js.map +1 -0
- package/dist/initiative-add-wu.js +180 -59
- package/dist/initiative-add-wu.js.map +1 -0
- package/dist/initiative-bulk-assign-wus.js +3 -1
- package/dist/initiative-bulk-assign-wus.js.map +1 -0
- package/dist/initiative-create.js +1 -0
- package/dist/initiative-create.js.map +1 -0
- package/dist/initiative-edit.js +67 -32
- package/dist/initiative-edit.js.map +1 -0
- package/dist/initiative-list.js +1 -0
- package/dist/initiative-list.js.map +1 -0
- package/dist/initiative-plan.js +1 -0
- package/dist/initiative-plan.js.map +1 -0
- package/dist/initiative-remove-wu.js +1 -0
- package/dist/initiative-remove-wu.js.map +1 -0
- package/dist/initiative-status.js +1 -0
- package/dist/initiative-status.js.map +1 -0
- package/dist/lane-health.js +1 -0
- package/dist/lane-health.js.map +1 -0
- package/dist/lane-suggest.js +1 -0
- package/dist/lane-suggest.js.map +1 -0
- package/dist/lumenflow-upgrade.js +1 -0
- package/dist/lumenflow-upgrade.js.map +1 -0
- package/dist/mem-checkpoint.js +1 -0
- package/dist/mem-checkpoint.js.map +1 -0
- package/dist/mem-cleanup.js +114 -1
- package/dist/mem-cleanup.js.map +1 -0
- package/dist/mem-context.js +1 -0
- package/dist/mem-context.js.map +1 -0
- package/dist/mem-create.js +1 -0
- package/dist/mem-create.js.map +1 -0
- package/dist/mem-delete.js +1 -0
- package/dist/mem-delete.js.map +1 -0
- package/dist/mem-export.js +1 -0
- package/dist/mem-export.js.map +1 -0
- package/dist/mem-inbox.js +1 -0
- package/dist/mem-inbox.js.map +1 -0
- package/dist/mem-index.js +1 -0
- package/dist/mem-index.js.map +1 -0
- package/dist/mem-init.js +1 -0
- package/dist/mem-init.js.map +1 -0
- package/dist/mem-profile.js +1 -0
- package/dist/mem-profile.js.map +1 -0
- package/dist/mem-promote.js +1 -0
- package/dist/mem-promote.js.map +1 -0
- package/dist/mem-ready.js +1 -0
- package/dist/mem-ready.js.map +1 -0
- package/dist/mem-recover.js +1 -0
- package/dist/mem-recover.js.map +1 -0
- package/dist/mem-signal.js +12 -1
- package/dist/mem-signal.js.map +1 -0
- package/dist/mem-start.js +1 -0
- package/dist/mem-start.js.map +1 -0
- package/dist/mem-summarize.js +1 -0
- package/dist/mem-summarize.js.map +1 -0
- package/dist/mem-triage.js +2 -1
- package/dist/mem-triage.js.map +1 -0
- package/dist/merge-block.js +1 -0
- package/dist/merge-block.js.map +1 -0
- package/dist/metrics-cli.js +1 -0
- package/dist/metrics-cli.js.map +1 -0
- package/dist/metrics-snapshot.js +1 -0
- package/dist/metrics-snapshot.js.map +1 -0
- package/dist/onboarding-smoke-test.js +1 -0
- package/dist/onboarding-smoke-test.js.map +1 -0
- package/dist/orchestrate-init-status.js +1 -0
- package/dist/orchestrate-init-status.js.map +1 -0
- package/dist/orchestrate-initiative.js +20 -1
- package/dist/orchestrate-initiative.js.map +1 -0
- package/dist/orchestrate-monitor.js +1 -0
- package/dist/orchestrate-monitor.js.map +1 -0
- package/dist/plan-create.js +1 -0
- package/dist/plan-create.js.map +1 -0
- package/dist/plan-edit.js +1 -0
- package/dist/plan-edit.js.map +1 -0
- package/dist/plan-link.js +1 -0
- package/dist/plan-link.js.map +1 -0
- package/dist/plan-promote.js +1 -0
- package/dist/plan-promote.js.map +1 -0
- package/dist/public-manifest.js +773 -0
- package/dist/public-manifest.js.map +1 -0
- package/dist/release.js +3 -2
- package/dist/release.js.map +1 -0
- package/dist/rotate-progress.js +2 -1
- package/dist/rotate-progress.js.map +1 -0
- package/dist/session-coordinator.js +1 -0
- package/dist/session-coordinator.js.map +1 -0
- package/dist/shared-validators.js +78 -0
- package/dist/shared-validators.js.map +1 -0
- package/dist/signal-cleanup.js +1 -0
- package/dist/signal-cleanup.js.map +1 -0
- package/dist/spawn-list.js +1 -0
- package/dist/spawn-list.js.map +1 -0
- package/dist/state-bootstrap.js +1 -0
- package/dist/state-bootstrap.js.map +1 -0
- package/dist/state-cleanup.js +1 -0
- package/dist/state-cleanup.js.map +1 -0
- package/dist/state-doctor-fix.js +37 -1
- package/dist/state-doctor-fix.js.map +1 -0
- package/dist/state-doctor.js +11 -6
- package/dist/state-doctor.js.map +1 -0
- package/dist/sync-templates.js +1 -0
- package/dist/sync-templates.js.map +1 -0
- package/dist/trace-gen.js +1 -0
- package/dist/trace-gen.js.map +1 -0
- package/dist/validate-agent-skills.js +1 -0
- package/dist/validate-agent-skills.js.map +1 -0
- package/dist/validate-agent-sync.js +1 -0
- package/dist/validate-agent-sync.js.map +1 -0
- package/dist/validate-backlog-sync.js +1 -0
- package/dist/validate-backlog-sync.js.map +1 -0
- package/dist/validate-skills-spec.js +1 -0
- package/dist/validate-skills-spec.js.map +1 -0
- package/dist/validate.js +1 -0
- package/dist/validate.js.map +1 -0
- package/dist/wu-block.js +1 -0
- package/dist/wu-block.js.map +1 -0
- package/dist/wu-claim-repair-guidance.js +10 -0
- package/dist/wu-claim-repair-guidance.js.map +1 -0
- package/dist/wu-claim.js +40 -0
- package/dist/wu-claim.js.map +1 -0
- package/dist/wu-cleanup.js +2 -1
- package/dist/wu-cleanup.js.map +1 -0
- package/dist/wu-create.js +91 -25
- package/dist/wu-create.js.map +1 -0
- package/dist/wu-delete.js +3 -2
- package/dist/wu-delete.js.map +1 -0
- package/dist/wu-deps.js +2 -1
- package/dist/wu-deps.js.map +1 -0
- package/dist/wu-done-auto-cleanup.js +1 -0
- package/dist/wu-done-auto-cleanup.js.map +1 -0
- package/dist/wu-done-check.js +1 -0
- package/dist/wu-done-check.js.map +1 -0
- package/dist/wu-done-decay.js +88 -0
- package/dist/wu-done-decay.js.map +1 -0
- package/dist/wu-done.js +75 -18
- package/dist/wu-done.js.map +1 -0
- package/dist/wu-edit.js +2 -1
- package/dist/wu-edit.js.map +1 -0
- package/dist/wu-infer-lane.js +1 -0
- package/dist/wu-infer-lane.js.map +1 -0
- package/dist/wu-preflight.js +1 -0
- package/dist/wu-preflight.js.map +1 -0
- package/dist/wu-prep.js +105 -9
- package/dist/wu-prep.js.map +1 -0
- package/dist/wu-proto.js +12 -9
- package/dist/wu-proto.js.map +1 -0
- package/dist/wu-prune.js +1 -0
- package/dist/wu-prune.js.map +1 -0
- package/dist/wu-recover.js +54 -2
- package/dist/wu-recover.js.map +1 -0
- package/dist/wu-release.js +2 -1
- package/dist/wu-release.js.map +1 -0
- package/dist/wu-repair.js +1 -0
- package/dist/wu-repair.js.map +1 -0
- package/dist/wu-spawn-completion.js +1 -0
- package/dist/wu-spawn-completion.js.map +1 -0
- package/dist/wu-spawn.js +3 -2
- package/dist/wu-spawn.js.map +1 -0
- package/dist/wu-status.js +1 -0
- package/dist/wu-status.js.map +1 -0
- package/dist/wu-unblock.js +1 -0
- package/dist/wu-unblock.js.map +1 -0
- package/dist/wu-unlock-lane.js +1 -0
- package/dist/wu-unlock-lane.js.map +1 -0
- package/dist/wu-validate.js +58 -9
- package/dist/wu-validate.js.map +1 -0
- package/package.json +11 -21
- package/templates/core/.husky/pre-commit.template +5 -5
- package/templates/core/.mcp.json.template +8 -0
- package/templates/core/LUMENFLOW.md.template +2 -2
- package/templates/core/ai/onboarding/agent-safety-card.md.template +6 -6
- package/templates/core/ai/onboarding/lumenflow-force-usage.md.template +4 -4
- package/templates/core/ai/onboarding/vendor-support.md.template +73 -0
- package/templates/core/scripts/safe-git.template +29 -0
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ This package provides CLI commands for the LumenFlow workflow framework, includi
|
|
|
101
101
|
|
|
102
102
|
| Command | Description |
|
|
103
103
|
| ---------------------------- | ------------------------------------------------------------------ |
|
|
104
|
-
| `initiative-add-wu` | Link
|
|
104
|
+
| `initiative-add-wu` | Link one or more WUs to an initiative bidirectionally |
|
|
105
105
|
| `initiative-bulk-assign-wus` | Bulk-assign orphaned WUs to initiatives based on lane prefix rules |
|
|
106
106
|
| `initiative-create` | Create a new Initiative with micro-worktree isolation (race-safe) |
|
|
107
107
|
| `initiative-edit` | Edit Initiative YAML files with micro-worktree isolation |
|
|
@@ -131,21 +131,15 @@ This package provides CLI commands for the LumenFlow workflow framework, includi
|
|
|
131
131
|
|
|
132
132
|
### Verification & Gates
|
|
133
133
|
|
|
134
|
-
| Command
|
|
135
|
-
|
|
|
136
|
-
| `gates`
|
|
137
|
-
| `lumenflow-gates` | Alias for `gates` - run quality gates with support for docs-only mode and tiered testing |
|
|
134
|
+
| Command | Description |
|
|
135
|
+
| ------- | ------------------------------------------------------------------------------------------ |
|
|
136
|
+
| `gates` | Run quality gates with support for docs-only mode, incremental linting, and tiered testing |
|
|
138
137
|
|
|
139
138
|
### System & Setup
|
|
140
139
|
|
|
141
140
|
| Command | Description |
|
|
142
141
|
| -------------------------- | --------------------------------------------------------------------------------- |
|
|
143
142
|
| `backlog-prune` | Backlog Prune Command |
|
|
144
|
-
| `deps-add` | Deps Add CLI Command |
|
|
145
|
-
| `deps-remove` | Deps Remove CLI Command |
|
|
146
|
-
| `guard-locked` | |
|
|
147
|
-
| `guard-main-branch` | Guard Main Branch CLI Tool |
|
|
148
|
-
| `guard-worktree-commit` | |
|
|
149
143
|
| `init-plan` | Link a plan file to an initiative |
|
|
150
144
|
| `lumenflow` | Initialize LumenFlow in a project\n\n |
|
|
151
145
|
| `lumenflow-commands` | List all available LumenFlow CLI commands |
|
|
@@ -162,19 +156,12 @@ This package provides CLI commands for the LumenFlow workflow framework, includi
|
|
|
162
156
|
| `plan-edit` | Edit a section in a plan file |
|
|
163
157
|
| `plan-link` | Link a plan file to a WU or initiative |
|
|
164
158
|
| `plan-promote` | Promote a plan to approved status |
|
|
165
|
-
| `rotate-progress` | Rotate Progress CLI Command |
|
|
166
|
-
| `session-coordinator` | Session Coordinator CLI Command |
|
|
167
159
|
| `signal-cleanup` | Prune old signals based on TTL policy to prevent unbounded growth |
|
|
168
160
|
| `state-bootstrap` | State Bootstrap Command |
|
|
169
161
|
| `state-cleanup` | Orchestrate all state cleanup: signals, memory, events |
|
|
170
162
|
| `state-doctor` | Check state integrity and optionally repair issues |
|
|
171
163
|
| `sync-templates` | Sync internal docs to CLI templates for release-cycle maintenance |
|
|
172
|
-
| `trace-gen` | Trace Generator CLI Command |
|
|
173
164
|
| `validate` | |
|
|
174
|
-
| `validate-agent-skills` | |
|
|
175
|
-
| `validate-agent-sync` | |
|
|
176
|
-
| `validate-backlog-sync` | |
|
|
177
|
-
| `validate-skills-spec` | |
|
|
178
165
|
|
|
179
166
|
### File & Git Operations
|
|
180
167
|
|
|
@@ -202,7 +189,7 @@ pnpm wu:done --id WU-123
|
|
|
202
189
|
|
|
203
190
|
# Memory operations
|
|
204
191
|
pnpm mem:checkpoint "Completed port definitions" --wu WU-123
|
|
205
|
-
pnpm mem:inbox --
|
|
192
|
+
pnpm mem:inbox --since 10m
|
|
206
193
|
|
|
207
194
|
# Initiative management
|
|
208
195
|
pnpm initiative:status INIT-007
|
|
@@ -245,6 +232,27 @@ The CLI integrates with other LumenFlow packages:
|
|
|
245
232
|
- `@lumenflow/agent` - Agent session management
|
|
246
233
|
- `@lumenflow/initiatives` - Initiative tracking
|
|
247
234
|
|
|
235
|
+
## MCP Server Setup (Claude Code)
|
|
236
|
+
|
|
237
|
+
LumenFlow provides an MCP (Model Context Protocol) server for deep integration with Claude Code.
|
|
238
|
+
|
|
239
|
+
When you run `lumenflow init --client claude`, a `.mcp.json` is automatically created:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"mcpServers": {
|
|
244
|
+
"lumenflow": {
|
|
245
|
+
"command": "npx",
|
|
246
|
+
"args": ["@lumenflow/mcp"]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The `@lumenflow/mcp` server provides tools for WU lifecycle, memory coordination, and lane management directly within Claude Code.
|
|
253
|
+
|
|
254
|
+
See [AI Integrations](https://lumenflow.dev/guides/ai-integrations) for full MCP documentation.
|
|
255
|
+
|
|
248
256
|
## Documentation
|
|
249
257
|
|
|
250
258
|
For complete documentation, see [lumenflow.dev](https://lumenflow.dev/reference/cli).
|
|
@@ -271,3 +279,5 @@ For detailed upgrade instructions, migration guides, and troubleshooting, see [U
|
|
|
271
279
|
## License
|
|
272
280
|
|
|
273
281
|
Apache-2.0
|
|
282
|
+
|
|
283
|
+
<!-- MODIFIED -->
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file commands.test.ts
|
|
3
3
|
* Tests for lumenflow commands discovery feature (WU-1378)
|
|
4
|
+
* Extended for public CLI manifest alignment (WU-1432)
|
|
4
5
|
*
|
|
5
|
-
* Tests the
|
|
6
|
-
* grouped by category with brief descriptions.
|
|
6
|
+
* Tests the commands subcommand that lists all available CLI commands
|
|
7
|
+
* grouped by category with brief descriptions. Also verifies alignment
|
|
8
|
+
* between public-manifest.ts, commands.ts, and package.json bin entries.
|
|
7
9
|
*/
|
|
8
10
|
import { describe, it, expect } from 'vitest';
|
|
9
11
|
import { getCommandsRegistry, formatCommandsOutput } from '../commands.js';
|
|
12
|
+
import { getPublicManifest, getPublicCommandNames, getPublicBinNames, isPublicCommand, } from '../public-manifest.js';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
18
|
+
// Script commands that are NOT CLI binaries (pnpm scripts only)
|
|
19
|
+
// These appear in the registry but should NOT be in the manifest
|
|
20
|
+
const SCRIPT_COMMANDS = new Set(['format', 'lint', 'typecheck', 'test', 'setup']);
|
|
10
21
|
describe('lumenflow commands', () => {
|
|
11
22
|
describe('getCommandsRegistry', () => {
|
|
12
23
|
it('should return command categories', () => {
|
|
@@ -73,3 +84,188 @@ describe('lumenflow commands', () => {
|
|
|
73
84
|
});
|
|
74
85
|
});
|
|
75
86
|
});
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// WU-1432: Public CLI Manifest Tests
|
|
89
|
+
// ============================================================================
|
|
90
|
+
describe('public CLI manifest (WU-1432)', () => {
|
|
91
|
+
describe('getPublicManifest', () => {
|
|
92
|
+
it('should return an array of public commands', () => {
|
|
93
|
+
const manifest = getPublicManifest();
|
|
94
|
+
expect(manifest).toBeDefined();
|
|
95
|
+
expect(Array.isArray(manifest)).toBe(true);
|
|
96
|
+
expect(manifest.length).toBeGreaterThan(0);
|
|
97
|
+
});
|
|
98
|
+
it('should have required fields for each command', () => {
|
|
99
|
+
const manifest = getPublicManifest();
|
|
100
|
+
for (const cmd of manifest) {
|
|
101
|
+
expect(cmd.name).toBeDefined();
|
|
102
|
+
expect(typeof cmd.name).toBe('string');
|
|
103
|
+
expect(cmd.name.length).toBeGreaterThan(0);
|
|
104
|
+
expect(cmd.binName).toBeDefined();
|
|
105
|
+
expect(typeof cmd.binName).toBe('string');
|
|
106
|
+
expect(cmd.binName.length).toBeGreaterThan(0);
|
|
107
|
+
expect(cmd.description).toBeDefined();
|
|
108
|
+
expect(typeof cmd.description).toBe('string');
|
|
109
|
+
expect(cmd.description.length).toBeGreaterThan(0);
|
|
110
|
+
expect(cmd.category).toBeDefined();
|
|
111
|
+
expect(typeof cmd.category).toBe('string');
|
|
112
|
+
expect(cmd.category.length).toBeGreaterThan(0);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
it('should include core public commands', () => {
|
|
116
|
+
const names = getPublicCommandNames();
|
|
117
|
+
// WU lifecycle - must be public
|
|
118
|
+
expect(names).toContain('wu:create');
|
|
119
|
+
expect(names).toContain('wu:claim');
|
|
120
|
+
expect(names).toContain('wu:done');
|
|
121
|
+
expect(names).toContain('wu:prep');
|
|
122
|
+
expect(names).toContain('wu:status');
|
|
123
|
+
// Gates - must be public
|
|
124
|
+
expect(names).toContain('gates');
|
|
125
|
+
expect(names).toContain('lumenflow');
|
|
126
|
+
// Memory - must be public
|
|
127
|
+
expect(names).toContain('mem:checkpoint');
|
|
128
|
+
expect(names).toContain('mem:inbox');
|
|
129
|
+
// Initiatives - must be public
|
|
130
|
+
expect(names).toContain('initiative:create');
|
|
131
|
+
expect(names).toContain('initiative:status');
|
|
132
|
+
});
|
|
133
|
+
it('should NOT include internal/maintainer commands', () => {
|
|
134
|
+
const names = getPublicCommandNames();
|
|
135
|
+
// Guards are internal
|
|
136
|
+
expect(names).not.toContain('guard-worktree-commit');
|
|
137
|
+
expect(names).not.toContain('guard-locked');
|
|
138
|
+
expect(names).not.toContain('guard-main-branch');
|
|
139
|
+
// Validation internals
|
|
140
|
+
expect(names).not.toContain('validate-agent-skills');
|
|
141
|
+
expect(names).not.toContain('validate-agent-sync');
|
|
142
|
+
expect(names).not.toContain('validate-backlog-sync');
|
|
143
|
+
expect(names).not.toContain('validate-skills-spec');
|
|
144
|
+
// Session internals
|
|
145
|
+
expect(names).not.toContain('session-coordinator');
|
|
146
|
+
expect(names).not.toContain('rotate-progress');
|
|
147
|
+
// Trace/debug internals
|
|
148
|
+
expect(names).not.toContain('trace-gen');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe('isPublicCommand', () => {
|
|
152
|
+
it('should return true for public commands', () => {
|
|
153
|
+
expect(isPublicCommand('wu:create')).toBe(true);
|
|
154
|
+
expect(isPublicCommand('wu:claim')).toBe(true);
|
|
155
|
+
expect(isPublicCommand('gates')).toBe(true);
|
|
156
|
+
expect(isPublicCommand('lumenflow')).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
it('should return false for internal commands', () => {
|
|
159
|
+
expect(isPublicCommand('guard-worktree-commit')).toBe(false);
|
|
160
|
+
expect(isPublicCommand('validate-agent-skills')).toBe(false);
|
|
161
|
+
expect(isPublicCommand('session-coordinator')).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('manifest alignment (WU-1432)', () => {
|
|
166
|
+
describe('commands.ts derives from manifest', () => {
|
|
167
|
+
it('should have all CLI registry commands in the public manifest (excluding script commands)', () => {
|
|
168
|
+
const registry = getCommandsRegistry();
|
|
169
|
+
const publicNames = new Set(getPublicCommandNames());
|
|
170
|
+
const registryNames = [];
|
|
171
|
+
for (const category of registry) {
|
|
172
|
+
for (const cmd of category.commands) {
|
|
173
|
+
// Skip script commands - they're not CLI binaries
|
|
174
|
+
if (!SCRIPT_COMMANDS.has(cmd.name)) {
|
|
175
|
+
registryNames.push(cmd.name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Every CLI command in the registry must be a public command
|
|
180
|
+
for (const name of registryNames) {
|
|
181
|
+
expect(publicNames.has(name), `Command "${name}" in registry but not in public manifest`).toBe(true);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
it('should have matching descriptions between manifest and registry', () => {
|
|
185
|
+
const manifest = getPublicManifest();
|
|
186
|
+
const registry = getCommandsRegistry();
|
|
187
|
+
// Build a map of registry descriptions
|
|
188
|
+
const registryDescriptions = new Map();
|
|
189
|
+
for (const category of registry) {
|
|
190
|
+
for (const cmd of category.commands) {
|
|
191
|
+
registryDescriptions.set(cmd.name, cmd.description);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// For commands that appear in both, descriptions should match
|
|
195
|
+
for (const cmd of manifest) {
|
|
196
|
+
const registryDesc = registryDescriptions.get(cmd.name);
|
|
197
|
+
if (registryDesc) {
|
|
198
|
+
expect(cmd.description, `Description mismatch for "${cmd.name}"`).toBe(registryDesc);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
it('should have matching categories between manifest and registry', () => {
|
|
203
|
+
const manifest = getPublicManifest();
|
|
204
|
+
const registry = getCommandsRegistry();
|
|
205
|
+
// Build a map of registry categories
|
|
206
|
+
const registryCategories = new Map();
|
|
207
|
+
for (const category of registry) {
|
|
208
|
+
for (const cmd of category.commands) {
|
|
209
|
+
registryCategories.set(cmd.name, category.name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// For commands that appear in both, categories should match
|
|
213
|
+
for (const cmd of manifest) {
|
|
214
|
+
const registryCategory = registryCategories.get(cmd.name);
|
|
215
|
+
if (registryCategory) {
|
|
216
|
+
expect(cmd.category, `Category mismatch for "${cmd.name}"`).toBe(registryCategory);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('package.json bin alignment', () => {
|
|
222
|
+
it('should only include public commands in bin', () => {
|
|
223
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
224
|
+
const binEntries = Object.keys(packageJson.bin || {});
|
|
225
|
+
const publicBinNames = new Set(getPublicBinNames());
|
|
226
|
+
// Every bin entry should be in the public manifest
|
|
227
|
+
for (const binName of binEntries) {
|
|
228
|
+
expect(publicBinNames.has(binName), `Bin "${binName}" is in package.json but not in public manifest - should it be internal?`).toBe(true);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
it('should have all public commands in bin', () => {
|
|
232
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
233
|
+
const binEntries = new Set(Object.keys(packageJson.bin || {}));
|
|
234
|
+
const publicManifest = getPublicManifest();
|
|
235
|
+
// Every public manifest command should have a bin entry
|
|
236
|
+
for (const cmd of publicManifest) {
|
|
237
|
+
expect(binEntries.has(cmd.binName), `Public command "${cmd.name}" (bin: ${cmd.binName}) missing from package.json bin`).toBe(true);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
it('should have correct file paths in bin', () => {
|
|
241
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
242
|
+
const publicManifest = getPublicManifest();
|
|
243
|
+
for (const cmd of publicManifest) {
|
|
244
|
+
const binPath = packageJson.bin?.[cmd.binName];
|
|
245
|
+
expect(binPath, `Missing bin path for ${cmd.binName}`).toBeDefined();
|
|
246
|
+
expect(binPath, `Bin path for ${cmd.binName} should start with ./dist/`).toMatch(/^\.\/dist\//);
|
|
247
|
+
expect(binPath, `Bin path for ${cmd.binName} should end with .js`).toMatch(/\.js$/);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
it('should not include internal commands in bin', () => {
|
|
251
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
252
|
+
const binEntries = Object.keys(packageJson.bin || {});
|
|
253
|
+
// These internal commands should NOT be in bin
|
|
254
|
+
const internalCommands = [
|
|
255
|
+
'guard-worktree-commit',
|
|
256
|
+
'guard-locked',
|
|
257
|
+
'guard-main-branch',
|
|
258
|
+
'validate-agent-skills',
|
|
259
|
+
'validate-agent-sync',
|
|
260
|
+
'validate-backlog-sync',
|
|
261
|
+
'validate-skills-spec',
|
|
262
|
+
'session-coordinator',
|
|
263
|
+
'rotate-progress',
|
|
264
|
+
'trace-gen',
|
|
265
|
+
];
|
|
266
|
+
for (const internalCmd of internalCommands) {
|
|
267
|
+
expect(binEntries.includes(internalCmd), `Internal command "${internalCmd}" should NOT be in package.json bin`).toBe(false);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file gates-integration-tests.test.ts
|
|
3
|
+
* @description Tests for gates infrastructure fixes (WU-1415)
|
|
4
|
+
*
|
|
5
|
+
* Bug 1: vitest --include is not a valid CLI option
|
|
6
|
+
* Bug 2: docs-only turbo filter uses directory names instead of package names
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { extractPackagesFromCodePaths, resolveDocsOnlyTestPlan } from '../gates.js';
|
|
10
|
+
describe('WU-1415: Gates infrastructure fixes', () => {
|
|
11
|
+
describe('Bug 1: vitest integration test command', () => {
|
|
12
|
+
it('should NOT use --include flag (vitest does not support it)', async () => {
|
|
13
|
+
// Import the module to inspect the command construction
|
|
14
|
+
// We need to verify that runIntegrationTests uses valid vitest syntax
|
|
15
|
+
//
|
|
16
|
+
// vitest run accepts positional glob patterns, NOT --include flags:
|
|
17
|
+
// WRONG: vitest run --include='**/*.integration.*'
|
|
18
|
+
// RIGHT: vitest run '**/*.integration.*'
|
|
19
|
+
//
|
|
20
|
+
// This test ensures we're using the correct vitest CLI syntax
|
|
21
|
+
const gatesModule = await import('../gates.js');
|
|
22
|
+
// The command construction happens in runIntegrationTests
|
|
23
|
+
// We can't directly test the internal function, but we can verify
|
|
24
|
+
// via the module's exported constants or by checking the implementation
|
|
25
|
+
// doesn't contain --include
|
|
26
|
+
// Read the source to verify no --include in vitest commands
|
|
27
|
+
const fs = await import('fs');
|
|
28
|
+
const path = await import('path');
|
|
29
|
+
const gatesPath = path.join(import.meta.dirname, '..', 'gates.ts');
|
|
30
|
+
const source = fs.readFileSync(gatesPath, 'utf-8');
|
|
31
|
+
// Find the runIntegrationTests function and check it doesn't use --include
|
|
32
|
+
const integrationTestMatch = source.match(/function runIntegrationTests[\s\S]*?^}/m);
|
|
33
|
+
if (integrationTestMatch) {
|
|
34
|
+
const functionBody = integrationTestMatch[0];
|
|
35
|
+
// vitest run should NOT have --include flags
|
|
36
|
+
expect(functionBody).not.toMatch(/vitest.*--include/);
|
|
37
|
+
// Instead, glob patterns should be positional args or via proper config
|
|
38
|
+
// The fix should pass patterns directly: vitest run 'pattern1' 'pattern2'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('Bug 2: docs-only turbo filter', () => {
|
|
43
|
+
describe('extractPackagesFromCodePaths', () => {
|
|
44
|
+
it('should extract scoped package names from packages/ paths', () => {
|
|
45
|
+
const codePaths = [
|
|
46
|
+
'packages/@lumenflow/cli/src/gates.ts',
|
|
47
|
+
'packages/@lumenflow/core/src/index.ts',
|
|
48
|
+
];
|
|
49
|
+
const packages = extractPackagesFromCodePaths(codePaths);
|
|
50
|
+
expect(packages).toContain('@lumenflow/cli');
|
|
51
|
+
expect(packages).toContain('@lumenflow/core');
|
|
52
|
+
});
|
|
53
|
+
it('should return empty array for apps/ paths that are not real turbo packages', () => {
|
|
54
|
+
// apps/docs/ directory name is 'docs' but the turbo package might be
|
|
55
|
+
// named differently (e.g., '@lumenflow/docs' or not exist at all)
|
|
56
|
+
//
|
|
57
|
+
// The current implementation returns 'docs' which causes turbo to fail:
|
|
58
|
+
// "No package found with name 'docs' in workspace"
|
|
59
|
+
//
|
|
60
|
+
// Fix: Either lookup actual package.json name or skip apps
|
|
61
|
+
const codePaths = ['apps/docs/src/content/docs/', 'apps/github-app/'];
|
|
62
|
+
const packages = extractPackagesFromCodePaths(codePaths);
|
|
63
|
+
// Current buggy behavior returns ['docs', 'github-app']
|
|
64
|
+
// Fixed behavior should either:
|
|
65
|
+
// - Return actual package names from package.json
|
|
66
|
+
// - Or return empty array (apps don't have turbo test tasks)
|
|
67
|
+
//
|
|
68
|
+
// For now, the fix should skip apps that aren't valid turbo packages
|
|
69
|
+
// because apps/docs has no test script and apps/github-app was deleted
|
|
70
|
+
expect(packages).not.toContain('docs');
|
|
71
|
+
expect(packages).not.toContain('github-app');
|
|
72
|
+
});
|
|
73
|
+
it('should handle mixed code_paths (packages + apps + docs)', () => {
|
|
74
|
+
const codePaths = [
|
|
75
|
+
'packages/@lumenflow/cli/src/file.ts',
|
|
76
|
+
'apps/docs/astro.config.mjs',
|
|
77
|
+
'docs/DISTRIBUTION.md',
|
|
78
|
+
];
|
|
79
|
+
const packages = extractPackagesFromCodePaths(codePaths);
|
|
80
|
+
// Should include the real package
|
|
81
|
+
expect(packages).toContain('@lumenflow/cli');
|
|
82
|
+
// Should NOT include apps (no valid turbo package)
|
|
83
|
+
expect(packages).not.toContain('docs');
|
|
84
|
+
// Should NOT include docs/ (not a package)
|
|
85
|
+
expect(packages.length).toBe(1);
|
|
86
|
+
});
|
|
87
|
+
it('should return empty array for pure docs paths', () => {
|
|
88
|
+
const codePaths = ['docs/01-product/product-lines.md', 'docs/DISTRIBUTION.md'];
|
|
89
|
+
const packages = extractPackagesFromCodePaths(codePaths);
|
|
90
|
+
expect(packages).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('resolveDocsOnlyTestPlan', () => {
|
|
94
|
+
it('should return skip mode for pure documentation WUs', () => {
|
|
95
|
+
const plan = resolveDocsOnlyTestPlan({
|
|
96
|
+
codePaths: ['docs/README.md', 'apps/docs/content/'],
|
|
97
|
+
});
|
|
98
|
+
expect(plan.mode).toBe('skip');
|
|
99
|
+
expect(plan.packages).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
it('should return filtered mode only for valid package paths', () => {
|
|
102
|
+
const plan = resolveDocsOnlyTestPlan({
|
|
103
|
+
codePaths: ['packages/@lumenflow/cli/src/gates.ts', 'apps/docs/content/'],
|
|
104
|
+
});
|
|
105
|
+
expect(plan.mode).toBe('filtered');
|
|
106
|
+
expect(plan.packages).toContain('@lumenflow/cli');
|
|
107
|
+
// apps/docs should not be included
|
|
108
|
+
expect(plan.packages).not.toContain('docs');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -67,6 +67,24 @@ describe('docs-structure', () => {
|
|
|
67
67
|
expect(fs.existsSync(path.join(tempDir, 'docs', 'tasks'))).toBe(true);
|
|
68
68
|
expect(fs.existsSync(path.join(tempDir, 'docs', DOCS_04_OPERATIONS))).toBe(false);
|
|
69
69
|
});
|
|
70
|
+
it('should scaffold WU template with resilient defaults (simple)', async () => {
|
|
71
|
+
const options = {
|
|
72
|
+
force: false,
|
|
73
|
+
full: true,
|
|
74
|
+
docsStructure: SIMPLE_DOCS_STRUCTURE,
|
|
75
|
+
};
|
|
76
|
+
await scaffoldProject(tempDir, options);
|
|
77
|
+
const templatePath = path.join(tempDir, 'docs', 'tasks', 'templates', 'wu-template.yaml');
|
|
78
|
+
expect(fs.existsSync(templatePath)).toBe(true);
|
|
79
|
+
const content = fs.readFileSync(templatePath, 'utf-8');
|
|
80
|
+
// Feature WUs should reference plan protocol by default (plan-less friendly).
|
|
81
|
+
expect(content).toContain('lumenflow://plans/WU-XXX-plan.md');
|
|
82
|
+
// Ensure non-empty notes to avoid strict spec-linter failures out of the box.
|
|
83
|
+
expect(content).not.toContain("notes: ''");
|
|
84
|
+
expect(content).toContain('notes:');
|
|
85
|
+
// Ensure manual test stub exists to prevent empty tests failures.
|
|
86
|
+
expect(content).toContain('Manual check:');
|
|
87
|
+
});
|
|
70
88
|
it('should scaffold arc42 structure with --docs-structure arc42', async () => {
|
|
71
89
|
const options = {
|
|
72
90
|
force: false,
|
|
@@ -77,6 +95,21 @@ describe('docs-structure', () => {
|
|
|
77
95
|
// Arc42 structure: docs/04-operations/tasks
|
|
78
96
|
expect(fs.existsSync(path.join(tempDir, 'docs', DOCS_04_OPERATIONS, 'tasks'))).toBe(true);
|
|
79
97
|
});
|
|
98
|
+
it('should scaffold WU template with resilient defaults (arc42)', async () => {
|
|
99
|
+
const options = {
|
|
100
|
+
force: false,
|
|
101
|
+
full: true,
|
|
102
|
+
docsStructure: ARC42_DOCS_STRUCTURE,
|
|
103
|
+
};
|
|
104
|
+
await scaffoldProject(tempDir, options);
|
|
105
|
+
const templatePath = path.join(tempDir, 'docs', DOCS_04_OPERATIONS, 'tasks', 'templates', 'wu-template.yaml');
|
|
106
|
+
expect(fs.existsSync(templatePath)).toBe(true);
|
|
107
|
+
const content = fs.readFileSync(templatePath, 'utf-8');
|
|
108
|
+
expect(content).toContain('lumenflow://plans/WU-XXX-plan.md');
|
|
109
|
+
expect(content).not.toContain("notes: ''");
|
|
110
|
+
expect(content).toContain('notes:');
|
|
111
|
+
expect(content).toContain('Manual check:');
|
|
112
|
+
});
|
|
80
113
|
it('should auto-detect arc42 when docs/04-operations exists', async () => {
|
|
81
114
|
// Create existing arc42 structure
|
|
82
115
|
fs.mkdirSync(path.join(tempDir, 'docs', DOCS_04_OPERATIONS), { recursive: true });
|