sequant 1.11.0 ā 1.13.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 +93 -7
- package/dist/bin/cli.js +12 -9
- package/dist/src/commands/doctor.js +25 -20
- package/dist/src/commands/init.js +152 -65
- package/dist/src/commands/logs.js +7 -6
- package/dist/src/commands/run.d.ts +13 -1
- package/dist/src/commands/run.js +75 -12
- package/dist/src/commands/stats.js +67 -48
- package/dist/src/commands/status.js +30 -12
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +4 -0
- package/dist/src/lib/ac-linter.d.ts +116 -0
- package/dist/src/lib/ac-linter.js +304 -0
- package/dist/src/lib/cli-ui.d.ts +196 -0
- package/dist/src/lib/cli-ui.js +544 -0
- package/dist/src/lib/content-analyzer.d.ts +89 -0
- package/dist/src/lib/content-analyzer.js +437 -0
- package/dist/src/lib/phase-signal.d.ts +94 -0
- package/dist/src/lib/phase-signal.js +171 -0
- package/dist/src/lib/plugin-version-sync.d.ts +26 -0
- package/dist/src/lib/plugin-version-sync.js +91 -0
- package/dist/src/lib/project-name.d.ts +40 -0
- package/dist/src/lib/project-name.js +191 -0
- package/dist/src/lib/semgrep.d.ts +136 -0
- package/dist/src/lib/semgrep.js +406 -0
- package/dist/src/lib/solve-comment-parser.d.ts +84 -0
- package/dist/src/lib/solve-comment-parser.js +200 -0
- package/dist/src/lib/stack-config.d.ts +51 -0
- package/dist/src/lib/stack-config.js +77 -0
- package/dist/src/lib/stacks.d.ts +66 -0
- package/dist/src/lib/stacks.js +332 -0
- package/dist/src/lib/templates.d.ts +2 -0
- package/dist/src/lib/templates.js +12 -3
- package/dist/src/lib/upstream/assessment.d.ts +70 -0
- package/dist/src/lib/upstream/assessment.js +385 -0
- package/dist/src/lib/upstream/index.d.ts +11 -0
- package/dist/src/lib/upstream/index.js +14 -0
- package/dist/src/lib/upstream/issues.d.ts +38 -0
- package/dist/src/lib/upstream/issues.js +267 -0
- package/dist/src/lib/upstream/relevance.d.ts +50 -0
- package/dist/src/lib/upstream/relevance.js +209 -0
- package/dist/src/lib/upstream/report.d.ts +29 -0
- package/dist/src/lib/upstream/report.js +391 -0
- package/dist/src/lib/upstream/types.d.ts +207 -0
- package/dist/src/lib/upstream/types.js +5 -0
- package/dist/src/lib/workflow/log-writer.d.ts +1 -1
- package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
- package/dist/src/lib/workflow/qa-cache.d.ts +199 -0
- package/dist/src/lib/workflow/qa-cache.js +440 -0
- package/dist/src/lib/workflow/run-log-schema.d.ts +34 -6
- package/dist/src/lib/workflow/run-log-schema.js +12 -1
- package/dist/src/lib/workflow/state-schema.d.ts +4 -4
- package/dist/src/lib/workflow/types.d.ts +4 -0
- package/package.json +6 -1
- package/templates/hooks/pre-tool.sh +6 -0
- package/templates/memory/constitution.md +1 -5
- package/templates/skills/_shared/references/prompt-templates.md +350 -0
- package/templates/skills/_shared/references/subagent-types.md +131 -0
- package/templates/skills/exec/SKILL.md +82 -0
- package/templates/skills/fullsolve/SKILL.md +19 -2
- package/templates/skills/loop/SKILL.md +3 -1
- package/templates/skills/qa/SKILL.md +79 -9
- package/templates/skills/qa/references/quality-gates.md +85 -1
- package/templates/skills/qa/references/semgrep-rules.md +207 -0
- package/templates/skills/qa/scripts/quality-checks.sh +525 -15
- package/templates/skills/spec/SKILL.md +322 -9
package/README.md
CHANGED
|
@@ -9,12 +9,31 @@ Solve GitHub issues with structured phases and quality gates ā from issue to m
|
|
|
9
9
|
|
|
10
10
|
## Quick Start
|
|
11
11
|
|
|
12
|
+
### Option A: Install as Claude Code Plugin (Recommended)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Add the Sequant marketplace
|
|
16
|
+
/plugin marketplace add admarble/sequant
|
|
17
|
+
|
|
18
|
+
# Install the plugin
|
|
19
|
+
/plugin install sequant
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then run setup:
|
|
23
|
+
```
|
|
24
|
+
/sequant:setup # Initialize worktrees directory
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Option B: Install via npm
|
|
28
|
+
|
|
12
29
|
```bash
|
|
13
30
|
# In your project directory
|
|
14
31
|
npx sequant init
|
|
15
32
|
npx sequant doctor # Verify setup
|
|
16
33
|
```
|
|
17
34
|
|
|
35
|
+
### Start Using
|
|
36
|
+
|
|
18
37
|
Then in Claude Code:
|
|
19
38
|
|
|
20
39
|
```
|
|
@@ -31,9 +50,20 @@ Or step-by-step:
|
|
|
31
50
|
|
|
32
51
|
### Prerequisites
|
|
33
52
|
|
|
53
|
+
**Required:**
|
|
34
54
|
- [Claude Code](https://claude.ai/code) ā AI coding assistant
|
|
35
55
|
- [GitHub CLI](https://cli.github.com/) ā run `gh auth login`
|
|
36
|
-
-
|
|
56
|
+
- Git (for worktree-based isolation)
|
|
57
|
+
|
|
58
|
+
**For npm installation:**
|
|
59
|
+
- Node.js 18+
|
|
60
|
+
|
|
61
|
+
**Optional MCP servers (enhanced features):**
|
|
62
|
+
- `chrome-devtools` ā enables `/test` for browser-based UI testing
|
|
63
|
+
- `sequential-thinking` ā enhanced reasoning for complex decisions
|
|
64
|
+
- `context7` ā library documentation lookup
|
|
65
|
+
|
|
66
|
+
> **Note:** Sequant is optimized for Node.js/TypeScript projects. The worktree workflow works with any git repository.
|
|
37
67
|
|
|
38
68
|
---
|
|
39
69
|
|
|
@@ -52,6 +82,24 @@ Sequant adds slash commands to Claude Code that enforce a structured workflow:
|
|
|
52
82
|
|
|
53
83
|
> `/test` is optional ā used for UI features when Chrome DevTools MCP is available.
|
|
54
84
|
|
|
85
|
+
### Worktree Isolation
|
|
86
|
+
|
|
87
|
+
Sequant uses Git worktrees to isolate each issue's work:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
your-project/ # Main repo (stays on main branch)
|
|
91
|
+
../worktrees/
|
|
92
|
+
feature/
|
|
93
|
+
123-add-login/ # Issue #123 worktree (feature branch)
|
|
94
|
+
456-fix-bug/ # Issue #456 worktree (feature branch)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Why worktrees?**
|
|
98
|
+
- Work on multiple issues simultaneously
|
|
99
|
+
- Never pollute your main branch
|
|
100
|
+
- Each issue has its own dependencies and build
|
|
101
|
+
- Safe to discard failed experiments
|
|
102
|
+
|
|
55
103
|
### Quality Gates
|
|
56
104
|
|
|
57
105
|
Every `/qa` runs automated checks:
|
|
@@ -59,6 +107,7 @@ Every `/qa` runs automated checks:
|
|
|
59
107
|
- **AC Adherence** ā Code verified against acceptance criteria
|
|
60
108
|
- **Type Safety** ā Detects `any`, `as any`, missing types
|
|
61
109
|
- **Security Scans** ā OWASP-style vulnerability detection
|
|
110
|
+
- **Semgrep Static Analysis** ā Stack-aware rulesets, custom rules via `.sequant/semgrep-rules.yaml`
|
|
62
111
|
- **Scope Analysis** ā Flags changes outside issue scope
|
|
63
112
|
- **Execution Evidence** ā Scripts/CLI must pass smoke tests
|
|
64
113
|
- **Test Quality** ā Validates test coverage and mock hygiene
|
|
@@ -115,9 +164,9 @@ npx sequant run 123 --base feature/dashboard # Custom base branch
|
|
|
115
164
|
|
|
116
165
|
| Command | Purpose |
|
|
117
166
|
|---------|---------|
|
|
118
|
-
| `/merger` | Multi-issue integration and merge |
|
|
119
167
|
| `/testgen` | Generate test stubs from spec |
|
|
120
168
|
| `/verify` | CLI/script execution verification |
|
|
169
|
+
| `/setup` | Initialize Sequant in a project |
|
|
121
170
|
|
|
122
171
|
### Utilities
|
|
123
172
|
|
|
@@ -126,6 +175,7 @@ npx sequant run 123 --base feature/dashboard # Custom base branch
|
|
|
126
175
|
| `/assess` | Issue triage and status assessment |
|
|
127
176
|
| `/docs` | Generate feature documentation |
|
|
128
177
|
| `/clean` | Repository cleanup |
|
|
178
|
+
| `/improve` | Codebase analysis and improvement discovery |
|
|
129
179
|
| `/security-review` | Deep security analysis |
|
|
130
180
|
| `/reflect` | Workflow improvement analysis |
|
|
131
181
|
|
|
@@ -141,9 +191,10 @@ npx sequant status # Show version and config
|
|
|
141
191
|
npx sequant run <issues...> # Execute workflow
|
|
142
192
|
npx sequant state <cmd> # Manage workflow state (init/rebuild/clean)
|
|
143
193
|
npx sequant stats # View local workflow analytics
|
|
194
|
+
npx sequant dashboard # Launch real-time workflow dashboard
|
|
144
195
|
```
|
|
145
196
|
|
|
146
|
-
See [Run Command Options](docs/run-command.md), [State Command](docs/state-command.md), and [Analytics](docs/analytics.md) for details.
|
|
197
|
+
See [Run Command Options](docs/reference/run-command.md), [State Command](docs/reference/state-command.md), and [Analytics](docs/reference/analytics.md) for details.
|
|
147
198
|
|
|
148
199
|
---
|
|
149
200
|
|
|
@@ -160,7 +211,7 @@ See [Run Command Options](docs/run-command.md), [State Command](docs/state-comma
|
|
|
160
211
|
}
|
|
161
212
|
```
|
|
162
213
|
|
|
163
|
-
See [Customization Guide](docs/customization.md) for all options.
|
|
214
|
+
See [Customization Guide](docs/guides/customization.md) for all options.
|
|
164
215
|
|
|
165
216
|
---
|
|
166
217
|
|
|
@@ -177,17 +228,52 @@ See [Customization Guide](docs/customization.md) for all options.
|
|
|
177
228
|
|
|
178
229
|
## Documentation
|
|
179
230
|
|
|
231
|
+
- [Quickstart](docs/guides/quickstart.md) ā 5-minute guide
|
|
232
|
+
- [Complete Workflow](docs/guides/workflow.md) ā Full workflow including post-QA patterns
|
|
180
233
|
- [Getting Started](docs/getting-started/installation.md)
|
|
234
|
+
- [What We've Built](docs/internal/what-weve-built.md) ā Comprehensive project overview
|
|
181
235
|
- [Workflow Concepts](docs/concepts/workflow-phases.md)
|
|
182
|
-
- [Run Command](docs/run-command.md)
|
|
183
|
-
- [
|
|
184
|
-
- [Customization](docs/customization.md)
|
|
236
|
+
- [Run Command](docs/reference/run-command.md)
|
|
237
|
+
- [Git Workflows](docs/guides/git-workflows.md)
|
|
238
|
+
- [Customization](docs/guides/customization.md)
|
|
239
|
+
- [Plugin Updates & Versioning](docs/internal/plugin-updates.md)
|
|
185
240
|
- [Troubleshooting](docs/troubleshooting.md)
|
|
186
241
|
|
|
187
242
|
Stack guides: [Next.js](docs/stacks/nextjs.md) Ā· [Rust](docs/stacks/rust.md) Ā· [Python](docs/stacks/python.md) Ā· [Go](docs/stacks/go.md)
|
|
188
243
|
|
|
189
244
|
---
|
|
190
245
|
|
|
246
|
+
## Feedback & Contributing
|
|
247
|
+
|
|
248
|
+
### Reporting Issues
|
|
249
|
+
|
|
250
|
+
- **Plugin issues:** [Plugin Feedback template](https://github.com/admarble/sequant/issues/new?template=plugin-feedback.yml)
|
|
251
|
+
- **Bug reports:** [Bug template](https://github.com/admarble/sequant/issues/new?template=bug.yml)
|
|
252
|
+
- **Feature requests:** [Feature template](https://github.com/admarble/sequant/issues/new?template=feature.yml)
|
|
253
|
+
- **Questions:** [GitHub Discussions](https://github.com/admarble/sequant/discussions)
|
|
254
|
+
|
|
255
|
+
### Using `/improve` for Feedback
|
|
256
|
+
|
|
257
|
+
Run `/improve` in Claude Code to analyze your codebase and create structured issues:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
/improve # Analyze entire codebase
|
|
261
|
+
/improve security # Focus on security concerns
|
|
262
|
+
/improve tests # Find test coverage gaps
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The skill will present findings and offer to create GitHub issues automatically.
|
|
266
|
+
|
|
267
|
+
### Contributing
|
|
268
|
+
|
|
269
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
270
|
+
|
|
271
|
+
### Telemetry
|
|
272
|
+
|
|
273
|
+
Sequant does not collect any usage telemetry. See [docs/reference/telemetry.md](docs/reference/telemetry.md) for details.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
191
277
|
## License
|
|
192
278
|
|
|
193
279
|
MIT
|
package/dist/bin/cli.js
CHANGED
|
@@ -11,6 +11,8 @@ import { dirname, resolve } from "path";
|
|
|
11
11
|
import { readFileSync } from "fs";
|
|
12
12
|
import { initCommand } from "../src/commands/init.js";
|
|
13
13
|
import { isLocalNodeModulesInstall } from "../src/lib/version-check.js";
|
|
14
|
+
import { configureUI, banner } from "../src/lib/cli-ui.js";
|
|
15
|
+
import { isCI, isStdoutTTY } from "../src/lib/tty.js";
|
|
14
16
|
// Read version from package.json dynamically
|
|
15
17
|
// Works from both source (bin/) and compiled (dist/bin/) locations
|
|
16
18
|
function getVersion() {
|
|
@@ -46,6 +48,15 @@ const program = new Command();
|
|
|
46
48
|
if (process.argv.includes("--no-color")) {
|
|
47
49
|
process.env.FORCE_COLOR = "0";
|
|
48
50
|
}
|
|
51
|
+
// Configure UI early based on environment and flags
|
|
52
|
+
configureUI({
|
|
53
|
+
noColor: process.argv.includes("--no-color") || !!process.env.NO_COLOR,
|
|
54
|
+
jsonMode: process.argv.includes("--json"),
|
|
55
|
+
verbose: process.argv.includes("--verbose") || process.argv.includes("-v"),
|
|
56
|
+
isTTY: isStdoutTTY(),
|
|
57
|
+
isCI: isCI(),
|
|
58
|
+
minimal: process.env.SEQUANT_MINIMAL === "1",
|
|
59
|
+
});
|
|
49
60
|
// Warn if running from local node_modules (not npx cache or global)
|
|
50
61
|
// This helps users who accidentally have a stale local install
|
|
51
62
|
if (!process.argv.includes("--quiet") && isLocalNodeModulesInstall()) {
|
|
@@ -176,14 +187,6 @@ stateCmd
|
|
|
176
187
|
program.parse();
|
|
177
188
|
// Show help if no command provided
|
|
178
189
|
if (!process.argv.slice(2).length) {
|
|
179
|
-
console.log(
|
|
180
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
181
|
-
ā ā
|
|
182
|
-
ā ${chalk.bold("Sequant")} - Quantize your development workflow ā
|
|
183
|
-
ā ā
|
|
184
|
-
ā Sequential AI phases with quality gates ā
|
|
185
|
-
ā ā
|
|
186
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
187
|
-
`));
|
|
190
|
+
console.log(banner());
|
|
188
191
|
program.help();
|
|
189
192
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { execSync } from "child_process";
|
|
6
|
+
import { ui, colors } from "../lib/cli-ui.js";
|
|
6
7
|
import { fileExists, isExecutable } from "../lib/fs.js";
|
|
7
8
|
import { getManifest } from "../lib/manifest.js";
|
|
8
9
|
import { commandExists, isGhAuthenticated, isNativeWindows, isWSL, checkOptionalMcpServers, getMcpServersConfig, OPTIONAL_MCP_SERVERS, } from "../lib/system.js";
|
|
@@ -67,7 +68,8 @@ export function checkClosedIssues() {
|
|
|
67
68
|
return missingCommitIssues;
|
|
68
69
|
}
|
|
69
70
|
export async function doctorCommand(options = {}) {
|
|
70
|
-
console.log(
|
|
71
|
+
console.log(ui.headerBox("SEQUANT HEALTH CHECK"));
|
|
72
|
+
console.log();
|
|
71
73
|
const checks = [];
|
|
72
74
|
// Track gh availability and auth for conditional checks later
|
|
73
75
|
let ghAvailable = false;
|
|
@@ -369,22 +371,22 @@ export async function doctorCommand(options = {}) {
|
|
|
369
371
|
}
|
|
370
372
|
}
|
|
371
373
|
}
|
|
372
|
-
// Display results
|
|
374
|
+
// Display results with status icons
|
|
373
375
|
let passCount = 0;
|
|
374
376
|
let warnCount = 0;
|
|
375
377
|
let failCount = 0;
|
|
376
378
|
for (const check of checks) {
|
|
377
|
-
const
|
|
378
|
-
?
|
|
379
|
+
const statusType = check.status === "pass"
|
|
380
|
+
? "success"
|
|
379
381
|
: check.status === "warn"
|
|
380
|
-
?
|
|
381
|
-
:
|
|
382
|
+
? "warning"
|
|
383
|
+
: "error";
|
|
382
384
|
const color = check.status === "pass"
|
|
383
|
-
?
|
|
385
|
+
? colors.success
|
|
384
386
|
: check.status === "warn"
|
|
385
|
-
?
|
|
386
|
-
:
|
|
387
|
-
console.log(
|
|
387
|
+
? colors.warning
|
|
388
|
+
: colors.error;
|
|
389
|
+
console.log(` ${ui.statusIcon(statusType)} ${chalk.bold(check.name)}: ${color(check.message)}`);
|
|
388
390
|
if (check.status === "pass")
|
|
389
391
|
passCount++;
|
|
390
392
|
else if (check.status === "warn")
|
|
@@ -392,21 +394,24 @@ export async function doctorCommand(options = {}) {
|
|
|
392
394
|
else
|
|
393
395
|
failCount++;
|
|
394
396
|
}
|
|
395
|
-
// Summary
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
console.log(chalk.yellow(` ā Warnings: ${warnCount}`));
|
|
400
|
-
if (failCount > 0)
|
|
401
|
-
console.log(chalk.red(` ā Failed: ${failCount}`));
|
|
397
|
+
// Summary with boxed output
|
|
398
|
+
const totalChecks = passCount + warnCount + failCount;
|
|
399
|
+
let summaryTitle;
|
|
400
|
+
let summaryMessage;
|
|
402
401
|
if (failCount > 0) {
|
|
403
|
-
|
|
402
|
+
summaryTitle = `${failCount} check${failCount > 1 ? "s" : ""} failed`;
|
|
403
|
+
summaryMessage = `Passed: ${passCount}/${totalChecks}\nWarnings: ${warnCount}\nFailed: ${failCount}\n\nRun \`sequant init\` to fix issues.`;
|
|
404
|
+
console.log("\n" + ui.errorBox(summaryTitle, summaryMessage));
|
|
404
405
|
process.exit(1);
|
|
405
406
|
}
|
|
406
407
|
else if (warnCount > 0) {
|
|
407
|
-
|
|
408
|
+
summaryTitle = `All checks passed (${warnCount} warning${warnCount > 1 ? "s" : ""})`;
|
|
409
|
+
summaryMessage = `Passed: ${passCount}/${totalChecks}\nWarnings: ${warnCount}\n\nSequant should work correctly.`;
|
|
410
|
+
console.log("\n" + ui.warningBox(summaryTitle, summaryMessage));
|
|
408
411
|
}
|
|
409
412
|
else {
|
|
410
|
-
|
|
413
|
+
summaryTitle = `All ${totalChecks} checks passed!`;
|
|
414
|
+
summaryMessage = `Your Sequant installation is healthy.`;
|
|
415
|
+
console.log("\n" + ui.successBox(summaryTitle, summaryMessage));
|
|
411
416
|
}
|
|
412
417
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import inquirer from "inquirer";
|
|
6
|
-
import {
|
|
6
|
+
import { ui, colors } from "../lib/cli-ui.js";
|
|
7
|
+
import { detectStack, detectAllStacks, getStackConfig, detectPackageManager, getPackageManagerCommands, STACKS, } from "../lib/stacks.js";
|
|
7
8
|
import { copyTemplates } from "../lib/templates.js";
|
|
8
9
|
import { createManifest } from "../lib/manifest.js";
|
|
9
10
|
import { saveConfig } from "../lib/config.js";
|
|
@@ -12,6 +13,7 @@ import { fileExists, ensureDir, readFile, writeFile } from "../lib/fs.js";
|
|
|
12
13
|
import { commandExists, isGhAuthenticated, getInstallHint, } from "../lib/system.js";
|
|
13
14
|
import { shouldUseInteractiveMode, getNonInteractiveReason, } from "../lib/tty.js";
|
|
14
15
|
import { checkAllDependencies, displayDependencyStatus, runSetupWizard, shouldRunSetupWizard, } from "../lib/wizard.js";
|
|
16
|
+
import { saveStackConfig } from "../lib/stack-config.js";
|
|
15
17
|
/**
|
|
16
18
|
* Check prerequisites and display warnings
|
|
17
19
|
*/
|
|
@@ -66,7 +68,9 @@ function logDefault(label, value) {
|
|
|
66
68
|
console.log(chalk.blue(`š¦ ${label}: ${value} (default)`));
|
|
67
69
|
}
|
|
68
70
|
export async function initCommand(options) {
|
|
69
|
-
|
|
71
|
+
// Show banner
|
|
72
|
+
console.log(ui.banner());
|
|
73
|
+
console.log(colors.success("\nInitializing Sequant...\n"));
|
|
70
74
|
// Determine if we should use interactive mode
|
|
71
75
|
const useInteractive = shouldUseInteractiveMode(options.interactive);
|
|
72
76
|
const skipPrompts = options.yes || !useInteractive;
|
|
@@ -139,54 +143,116 @@ export async function initCommand(options) {
|
|
|
139
143
|
}
|
|
140
144
|
// Detect or prompt for stack
|
|
141
145
|
let stack = options.stack;
|
|
146
|
+
let additionalStacks = [];
|
|
142
147
|
if (!stack) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
// Check for multi-stack project in interactive mode
|
|
149
|
+
const allDetectedStacks = !skipPrompts ? await detectAllStacks() : [];
|
|
150
|
+
if (allDetectedStacks.length > 1 && !skipPrompts) {
|
|
151
|
+
// Multi-stack project detected - show checkbox selection
|
|
152
|
+
console.log(chalk.blue(`\nš Detected ${allDetectedStacks.length} stacks in this project:`));
|
|
153
|
+
for (const ds of allDetectedStacks) {
|
|
154
|
+
const location = ds.path ? ` (${ds.path}/)` : " (root)";
|
|
155
|
+
console.log(chalk.gray(` ⢠${STACKS[ds.stack]?.displayName || ds.stack}${location}`));
|
|
156
|
+
}
|
|
157
|
+
console.log();
|
|
158
|
+
// Multi-stack selection prompt
|
|
159
|
+
const { selectedStacks } = await inquirer.prompt([
|
|
150
160
|
{
|
|
151
|
-
type: "
|
|
152
|
-
name: "
|
|
153
|
-
message:
|
|
154
|
-
choices:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
type: "checkbox",
|
|
162
|
+
name: "selectedStacks",
|
|
163
|
+
message: "Select stacks to include in your constitution:",
|
|
164
|
+
choices: allDetectedStacks.map((ds) => ({
|
|
165
|
+
name: `${STACKS[ds.stack]?.displayName || ds.stack}${ds.path ? ` (${ds.path}/)` : " (root)"}`,
|
|
166
|
+
value: ds.stack,
|
|
167
|
+
checked: true, // Pre-select all detected stacks
|
|
168
|
+
})),
|
|
169
|
+
validate: (answer) => {
|
|
170
|
+
if (answer.length < 1) {
|
|
171
|
+
return "You must select at least one stack.";
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
},
|
|
158
175
|
},
|
|
159
176
|
]);
|
|
160
|
-
stack
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
177
|
+
// First selected stack is the primary
|
|
178
|
+
if (selectedStacks.length > 0) {
|
|
179
|
+
stack = selectedStacks[0];
|
|
180
|
+
additionalStacks = selectedStacks.slice(1);
|
|
181
|
+
}
|
|
182
|
+
// Confirm or change primary stack if multiple selected
|
|
183
|
+
if (selectedStacks.length > 1) {
|
|
184
|
+
const { primaryStack } = await inquirer.prompt([
|
|
185
|
+
{
|
|
186
|
+
type: "list",
|
|
187
|
+
name: "primaryStack",
|
|
188
|
+
message: "Which stack should be the primary? (determines dev URL and commands)",
|
|
189
|
+
choices: selectedStacks.map((s) => ({
|
|
190
|
+
name: STACKS[s]?.displayName || s,
|
|
191
|
+
value: s,
|
|
192
|
+
})),
|
|
193
|
+
},
|
|
194
|
+
]);
|
|
195
|
+
stack = primaryStack;
|
|
196
|
+
additionalStacks = selectedStacks.filter((s) => s !== primaryStack);
|
|
197
|
+
}
|
|
166
198
|
}
|
|
167
|
-
else
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
199
|
+
else {
|
|
200
|
+
// Single stack detection (original behavior)
|
|
201
|
+
const detected = await detectStack();
|
|
202
|
+
if (detected && skipPrompts) {
|
|
203
|
+
stack = detected;
|
|
204
|
+
logDefault("Detected stack", stack);
|
|
205
|
+
}
|
|
206
|
+
else if (detected && !skipPrompts) {
|
|
207
|
+
const { confirmedStack } = await inquirer.prompt([
|
|
208
|
+
{
|
|
209
|
+
type: "list",
|
|
210
|
+
name: "confirmedStack",
|
|
211
|
+
message: `Detected ${detected} project. Is this correct?`,
|
|
212
|
+
choices: [
|
|
213
|
+
{ name: `Yes, use ${detected}`, value: detected },
|
|
214
|
+
{ name: "No, let me choose", value: null },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
]);
|
|
218
|
+
stack = confirmedStack;
|
|
219
|
+
}
|
|
220
|
+
if (!stack && skipPrompts) {
|
|
221
|
+
// No detection and skipping prompts: use generic as default
|
|
222
|
+
stack = "generic";
|
|
223
|
+
logDefault("Using stack", stack);
|
|
224
|
+
}
|
|
225
|
+
else if (!stack) {
|
|
226
|
+
const { selectedStack } = await inquirer.prompt([
|
|
227
|
+
{
|
|
228
|
+
type: "list",
|
|
229
|
+
name: "selectedStack",
|
|
230
|
+
message: "Select your project stack:",
|
|
231
|
+
choices: [
|
|
232
|
+
{ name: "Next.js / React", value: "nextjs" },
|
|
233
|
+
{ name: "Astro", value: "astro" },
|
|
234
|
+
{ name: "SvelteKit", value: "sveltekit" },
|
|
235
|
+
{ name: "Remix", value: "remix" },
|
|
236
|
+
{ name: "Nuxt", value: "nuxt" },
|
|
237
|
+
{ name: "Rust", value: "rust" },
|
|
238
|
+
{ name: "Python", value: "python" },
|
|
239
|
+
{ name: "Go", value: "go" },
|
|
240
|
+
{ name: "Generic (no stack-specific config)", value: "generic" },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
]);
|
|
244
|
+
stack = selectedStack;
|
|
245
|
+
}
|
|
187
246
|
}
|
|
188
247
|
}
|
|
189
|
-
|
|
248
|
+
// Display selected stacks
|
|
249
|
+
if (additionalStacks.length > 0) {
|
|
250
|
+
console.log(chalk.blue(`\nš Primary Stack: ${stack}`));
|
|
251
|
+
console.log(chalk.blue(` Additional: ${additionalStacks.join(", ")}`));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log(chalk.blue(`\nš Stack: ${stack}`));
|
|
255
|
+
}
|
|
190
256
|
// Detect package manager
|
|
191
257
|
const packageManager = await detectPackageManager();
|
|
192
258
|
if (packageManager) {
|
|
@@ -236,21 +302,24 @@ export async function initCommand(options) {
|
|
|
236
302
|
return;
|
|
237
303
|
}
|
|
238
304
|
}
|
|
239
|
-
// Create directories
|
|
240
|
-
|
|
305
|
+
// Create directories with spinner
|
|
306
|
+
const dirSpinner = ui.spinner("Creating directories...");
|
|
307
|
+
dirSpinner.start();
|
|
241
308
|
await ensureDir(".claude/skills");
|
|
242
309
|
await ensureDir(".claude/hooks");
|
|
243
310
|
await ensureDir(".claude/memory");
|
|
244
311
|
await ensureDir(".claude/.sequant");
|
|
245
312
|
await ensureDir(".sequant/logs");
|
|
246
313
|
await ensureDir("scripts/dev");
|
|
314
|
+
dirSpinner.succeed("Created directories");
|
|
247
315
|
// Update .gitignore
|
|
248
316
|
const gitignoreUpdated = await updateGitignore();
|
|
249
317
|
if (gitignoreUpdated) {
|
|
250
|
-
|
|
318
|
+
ui.printStatus("success", "Updated .gitignore with Sequant entries");
|
|
251
319
|
}
|
|
252
320
|
// Save config with tokens
|
|
253
|
-
|
|
321
|
+
const configSpinner = ui.spinner("Saving configuration...");
|
|
322
|
+
configSpinner.start();
|
|
254
323
|
const pmConfig = packageManager
|
|
255
324
|
? getPackageManagerCommands(packageManager)
|
|
256
325
|
: getPackageManagerCommands("npm");
|
|
@@ -263,18 +332,33 @@ export async function initCommand(options) {
|
|
|
263
332
|
stack: stack,
|
|
264
333
|
initialized: new Date().toISOString(),
|
|
265
334
|
});
|
|
335
|
+
configSpinner.succeed("Saved configuration");
|
|
336
|
+
// Save multi-stack configuration if additional stacks selected
|
|
337
|
+
if (additionalStacks.length > 0) {
|
|
338
|
+
const stackConfig = {
|
|
339
|
+
primary: { name: stack },
|
|
340
|
+
additional: additionalStacks.map((name) => ({ name })),
|
|
341
|
+
};
|
|
342
|
+
await saveStackConfig(stackConfig);
|
|
343
|
+
console.log(chalk.blue("š Saved multi-stack configuration"));
|
|
344
|
+
}
|
|
266
345
|
// Create default settings
|
|
267
|
-
|
|
346
|
+
const settingsSpinner = ui.spinner("Creating default settings...");
|
|
347
|
+
settingsSpinner.start();
|
|
268
348
|
await createDefaultSettings();
|
|
349
|
+
settingsSpinner.succeed("Created default settings");
|
|
269
350
|
// Copy templates (with symlinks for scripts unless --no-symlinks)
|
|
270
|
-
|
|
351
|
+
const templatesSpinner = ui.spinner("Copying templates...");
|
|
352
|
+
templatesSpinner.start();
|
|
271
353
|
const { scriptsSymlinked, symlinkResults } = await copyTemplates(stack, tokens, {
|
|
272
354
|
noSymlinks: options.noSymlinks,
|
|
273
355
|
force: options.force,
|
|
356
|
+
additionalStacks,
|
|
274
357
|
});
|
|
358
|
+
templatesSpinner.succeed("Copied templates");
|
|
275
359
|
// Report symlink status
|
|
276
360
|
if (scriptsSymlinked) {
|
|
277
|
-
|
|
361
|
+
ui.printStatus("success", "Created symlinks for scripts/dev/");
|
|
278
362
|
}
|
|
279
363
|
else if (!options.noSymlinks && symlinkResults) {
|
|
280
364
|
// Some symlinks may have fallen back to copies
|
|
@@ -294,8 +378,10 @@ export async function initCommand(options) {
|
|
|
294
378
|
}
|
|
295
379
|
}
|
|
296
380
|
// Create manifest
|
|
297
|
-
|
|
381
|
+
const manifestSpinner = ui.spinner("Creating manifest...");
|
|
382
|
+
manifestSpinner.start();
|
|
298
383
|
await createManifest(stack, packageManager ?? undefined);
|
|
384
|
+
manifestSpinner.succeed("Created manifest");
|
|
299
385
|
// Build optional suggestions section
|
|
300
386
|
const optionalSuggestions = suggestions.filter((s) => s.startsWith("Optional"));
|
|
301
387
|
const optionalSection = optionalSuggestions.length > 0
|
|
@@ -306,19 +392,20 @@ export async function initCommand(options) {
|
|
|
306
392
|
const prereqReminder = hasRemainingIssues
|
|
307
393
|
? `\n${chalk.yellow("ā ļø Remember to install missing dependencies before using issue workflows.")}\n${chalk.gray(" Run 'sequant doctor' to verify your setup.\n")}`
|
|
308
394
|
: "";
|
|
309
|
-
// Success message
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
${chalk.bold("Next steps:")}
|
|
314
|
-
1. Review .claude/memory/constitution.md and customize for your project
|
|
315
|
-
2. Start using workflow commands in Claude Code:
|
|
395
|
+
// Success message with boxed output
|
|
396
|
+
const nextStepsContent = `${chalk.bold("Next steps:")}
|
|
397
|
+
1. Review .claude/memory/constitution.md
|
|
398
|
+
2. Start using workflow commands:
|
|
316
399
|
|
|
317
|
-
${chalk.cyan("/spec 123")}
|
|
318
|
-
${chalk.cyan("/exec 123")}
|
|
319
|
-
${chalk.cyan("/qa 123")}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
400
|
+
${chalk.cyan("/spec 123")} - Plan implementation
|
|
401
|
+
${chalk.cyan("/exec 123")} - Implement the feature
|
|
402
|
+
${chalk.cyan("/qa 123")} - Quality review`;
|
|
403
|
+
console.log("\n" + ui.successBox("Sequant initialized successfully!", nextStepsContent));
|
|
404
|
+
if (prereqReminder) {
|
|
405
|
+
console.log(prereqReminder);
|
|
406
|
+
}
|
|
407
|
+
if (optionalSection) {
|
|
408
|
+
console.log(optionalSection);
|
|
409
|
+
}
|
|
410
|
+
console.log(chalk.gray("\nDocumentation: https://github.com/admarble/sequant#readme\n"));
|
|
324
411
|
}
|
|
@@ -7,6 +7,7 @@ import chalk from "chalk";
|
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import * as path from "path";
|
|
9
9
|
import * as os from "os";
|
|
10
|
+
import { ui, colors } from "../lib/cli-ui.js";
|
|
10
11
|
import { RunLogSchema, LOG_PATHS, } from "../lib/workflow/run-log-schema.js";
|
|
11
12
|
import { manualRotate, getLogStats, formatBytes, } from "../lib/workflow/log-rotation.js";
|
|
12
13
|
import { getSettings } from "../lib/settings.js";
|
|
@@ -177,17 +178,17 @@ export async function logsCommand(options) {
|
|
|
177
178
|
await handleRotation(logDir, options.dryRun ?? false);
|
|
178
179
|
return;
|
|
179
180
|
}
|
|
180
|
-
console.log(
|
|
181
|
-
console.log(
|
|
181
|
+
console.log(ui.headerBox("SEQUANT RUN LOGS"));
|
|
182
|
+
console.log(colors.muted(`\n Log directory: ${logDir}`));
|
|
182
183
|
// List log files
|
|
183
184
|
const logFiles = listLogFiles(logDir);
|
|
184
185
|
if (logFiles.length === 0) {
|
|
185
|
-
console.log(
|
|
186
|
-
console.log(
|
|
186
|
+
console.log(colors.warning("\n No logs found."));
|
|
187
|
+
console.log(colors.muted(" Run `npx sequant run <issues>` to generate logs."));
|
|
187
188
|
console.log("");
|
|
188
189
|
return;
|
|
189
190
|
}
|
|
190
|
-
console.log(
|
|
191
|
+
console.log(colors.muted(` Found ${logFiles.length} log file(s)\n`));
|
|
191
192
|
// Parse all logs
|
|
192
193
|
const logs = logFiles
|
|
193
194
|
.map((filename) => {
|
|
@@ -215,7 +216,7 @@ export async function logsCommand(options) {
|
|
|
215
216
|
displayLogSummary(log, filename);
|
|
216
217
|
}
|
|
217
218
|
// Summary
|
|
218
|
-
console.log(
|
|
219
|
+
console.log("\n" + ui.divider());
|
|
219
220
|
const totalPassed = filteredLogs.reduce((sum, { log }) => sum + log.summary.passed, 0);
|
|
220
221
|
const totalFailed = filteredLogs.reduce((sum, { log }) => sum + log.summary.failed, 0);
|
|
221
222
|
const totalDuration = filteredLogs.reduce((sum, { log }) => sum + log.summary.totalDurationSeconds, 0);
|