sequant 1.5.2 ā 1.5.4
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/dist/bin/cli.js +1 -0
- package/dist/src/commands/doctor.js +30 -0
- package/dist/src/commands/run.d.ts +2 -0
- package/dist/src/commands/run.js +14 -0
- package/dist/src/commands/status.js +2 -2
- package/dist/src/commands/update.js +13 -2
- package/dist/src/lib/manifest.d.ts +1 -0
- package/dist/src/lib/manifest.js +25 -1
- package/dist/src/lib/version-check.d.ts +67 -0
- package/dist/src/lib/version-check.js +215 -0
- package/package.json +1 -1
- package/templates/skills/qa/SKILL.md +8 -13
- package/templates/skills/spec/SKILL.md +6 -11
package/dist/bin/cli.js
CHANGED
|
@@ -89,6 +89,7 @@ program
|
|
|
89
89
|
.option("--smart-tests", "Enable smart test detection (default)")
|
|
90
90
|
.option("--no-smart-tests", "Disable smart test detection")
|
|
91
91
|
.option("--testgen", "Run testgen phase after spec")
|
|
92
|
+
.option("--quiet", "Suppress version warnings and non-essential output")
|
|
92
93
|
.action(runCommand);
|
|
93
94
|
program
|
|
94
95
|
.command("logs")
|
|
@@ -5,9 +5,39 @@ import chalk from "chalk";
|
|
|
5
5
|
import { fileExists, isExecutable } from "../lib/fs.js";
|
|
6
6
|
import { getManifest } from "../lib/manifest.js";
|
|
7
7
|
import { commandExists, isGhAuthenticated, isNativeWindows, isWSL, checkOptionalMcpServers, OPTIONAL_MCP_SERVERS, } from "../lib/system.js";
|
|
8
|
+
import { checkVersionThorough, getVersionWarning, } from "../lib/version-check.js";
|
|
8
9
|
export async function doctorCommand() {
|
|
9
10
|
console.log(chalk.blue("\nš Running health checks...\n"));
|
|
10
11
|
const checks = [];
|
|
12
|
+
// Check 0: Version freshness
|
|
13
|
+
const versionResult = await checkVersionThorough();
|
|
14
|
+
if (versionResult.latestVersion) {
|
|
15
|
+
if (versionResult.isOutdated) {
|
|
16
|
+
checks.push({
|
|
17
|
+
name: "Version",
|
|
18
|
+
status: "warn",
|
|
19
|
+
message: `Outdated: ${versionResult.currentVersion} ā ${versionResult.latestVersion} available`,
|
|
20
|
+
});
|
|
21
|
+
// Show remediation steps
|
|
22
|
+
console.log(chalk.yellow(` ā ļø ${getVersionWarning(versionResult.currentVersion, versionResult.latestVersion)}`));
|
|
23
|
+
console.log("");
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
checks.push({
|
|
27
|
+
name: "Version",
|
|
28
|
+
status: "pass",
|
|
29
|
+
message: `Up to date (${versionResult.currentVersion})`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Could not fetch version - skip this check silently (graceful degradation)
|
|
35
|
+
checks.push({
|
|
36
|
+
name: "Version",
|
|
37
|
+
status: "pass",
|
|
38
|
+
message: `${versionResult.currentVersion} (could not verify latest)`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
11
41
|
// Check 1: Manifest exists
|
|
12
42
|
const manifest = await getManifest();
|
|
13
43
|
if (manifest) {
|
|
@@ -56,6 +56,8 @@ interface RunOptions {
|
|
|
56
56
|
worktreeIsolation?: boolean;
|
|
57
57
|
/** Reuse existing worktrees instead of creating new ones */
|
|
58
58
|
reuseWorktrees?: boolean;
|
|
59
|
+
/** Suppress version warnings and non-essential output */
|
|
60
|
+
quiet?: boolean;
|
|
59
61
|
}
|
|
60
62
|
/**
|
|
61
63
|
* Main run command
|
package/dist/src/commands/run.js
CHANGED
|
@@ -15,6 +15,7 @@ import { PM_CONFIG } from "../lib/stacks.js";
|
|
|
15
15
|
import { LogWriter, createPhaseLogFromTiming, } from "../lib/workflow/log-writer.js";
|
|
16
16
|
import { DEFAULT_PHASES, DEFAULT_CONFIG, } from "../lib/workflow/types.js";
|
|
17
17
|
import { ShutdownManager } from "../lib/shutdown.js";
|
|
18
|
+
import { checkVersionCached, getVersionWarning } from "../lib/version-check.js";
|
|
18
19
|
/**
|
|
19
20
|
* Slugify a title for branch naming
|
|
20
21
|
*/
|
|
@@ -726,6 +727,19 @@ function parseBatches(batchArgs) {
|
|
|
726
727
|
*/
|
|
727
728
|
export async function runCommand(issues, options) {
|
|
728
729
|
console.log(chalk.blue("\nš Sequant Workflow Execution\n"));
|
|
730
|
+
// Version freshness check (cached, non-blocking, respects --quiet)
|
|
731
|
+
if (!options.quiet) {
|
|
732
|
+
try {
|
|
733
|
+
const versionResult = await checkVersionCached();
|
|
734
|
+
if (versionResult.isOutdated && versionResult.latestVersion) {
|
|
735
|
+
console.log(chalk.yellow(` ā ļø ${getVersionWarning(versionResult.currentVersion, versionResult.latestVersion)}`));
|
|
736
|
+
console.log("");
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
catch {
|
|
740
|
+
// Silent failure - version check is non-critical
|
|
741
|
+
}
|
|
742
|
+
}
|
|
729
743
|
// Check if initialized
|
|
730
744
|
const manifest = await getManifest();
|
|
731
745
|
if (!manifest) {
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* sequant status - Show version and configuration
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
-
import { getManifest } from "../lib/manifest.js";
|
|
5
|
+
import { getManifest, getPackageVersion } from "../lib/manifest.js";
|
|
6
6
|
import { fileExists } from "../lib/fs.js";
|
|
7
7
|
import { readdir } from "fs/promises";
|
|
8
8
|
export async function statusCommand() {
|
|
9
9
|
console.log(chalk.bold("\nš Sequant Status\n"));
|
|
10
10
|
// Package version
|
|
11
|
-
console.log(chalk.gray(
|
|
11
|
+
console.log(chalk.gray(`Package version: ${getPackageVersion()}`));
|
|
12
12
|
// Check initialization
|
|
13
13
|
const manifest = await getManifest();
|
|
14
14
|
if (!manifest) {
|
|
@@ -5,7 +5,7 @@ import chalk from "chalk";
|
|
|
5
5
|
import { diffLines } from "diff";
|
|
6
6
|
import inquirer from "inquirer";
|
|
7
7
|
import { spawnSync } from "child_process";
|
|
8
|
-
import { getManifest, updateManifest } from "../lib/manifest.js";
|
|
8
|
+
import { getManifest, updateManifest, getPackageVersion, } from "../lib/manifest.js";
|
|
9
9
|
import { getTemplateContent, listTemplateFiles, processTemplate, } from "../lib/templates.js";
|
|
10
10
|
import { getConfig, saveConfig } from "../lib/config.js";
|
|
11
11
|
import { getStackConfig, PM_CONFIG, getPackageManagerCommands, } from "../lib/stacks.js";
|
|
@@ -18,8 +18,19 @@ export async function updateCommand(options) {
|
|
|
18
18
|
console.log(chalk.red("ā Sequant is not initialized. Run `sequant init` first."));
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
+
const packageVersion = getPackageVersion();
|
|
21
22
|
console.log(chalk.gray(`Current version: ${manifest.version}`));
|
|
22
|
-
console.log(chalk.gray(`Stack: ${manifest.stack}
|
|
23
|
+
console.log(chalk.gray(`Stack: ${manifest.stack}`));
|
|
24
|
+
console.log(chalk.gray(`CLI version: ${packageVersion}\n`));
|
|
25
|
+
// Warn if running an older CLI version than what's installed
|
|
26
|
+
const [mMajor, mMinor, mPatch] = manifest.version.split(".").map(Number);
|
|
27
|
+
const [pMajor, pMinor, pPatch] = packageVersion.split(".").map(Number);
|
|
28
|
+
const manifestNum = mMajor * 10000 + mMinor * 100 + mPatch;
|
|
29
|
+
const packageNum = pMajor * 10000 + pMinor * 100 + pPatch;
|
|
30
|
+
if (packageNum < manifestNum) {
|
|
31
|
+
console.log(chalk.yellow(`ā ļø Warning: You're running an older CLI version (${packageVersion}) than installed (${manifest.version}).`));
|
|
32
|
+
console.log(chalk.yellow(` Run with: npx sequant@latest update\n`));
|
|
33
|
+
}
|
|
23
34
|
// Get config with tokens (or migrate legacy installs)
|
|
24
35
|
let config = await getConfig();
|
|
25
36
|
let tokens;
|
package/dist/src/lib/manifest.js
CHANGED
|
@@ -29,6 +29,26 @@ function findPackageJson() {
|
|
|
29
29
|
}
|
|
30
30
|
const pkg = JSON.parse(findPackageJson());
|
|
31
31
|
const PACKAGE_VERSION = pkg.version;
|
|
32
|
+
export function getPackageVersion() {
|
|
33
|
+
return PACKAGE_VERSION;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compare two semver versions.
|
|
37
|
+
* Returns: 1 if a > b, -1 if a < b, 0 if equal
|
|
38
|
+
*/
|
|
39
|
+
function compareVersions(a, b) {
|
|
40
|
+
const partsA = a.split(".").map(Number);
|
|
41
|
+
const partsB = b.split(".").map(Number);
|
|
42
|
+
for (let i = 0; i < 3; i++) {
|
|
43
|
+
const numA = partsA[i] || 0;
|
|
44
|
+
const numB = partsB[i] || 0;
|
|
45
|
+
if (numA > numB)
|
|
46
|
+
return 1;
|
|
47
|
+
if (numA < numB)
|
|
48
|
+
return -1;
|
|
49
|
+
}
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
32
52
|
export async function getManifest() {
|
|
33
53
|
if (!(await fileExists(MANIFEST_PATH))) {
|
|
34
54
|
return null;
|
|
@@ -56,7 +76,11 @@ export async function updateManifest() {
|
|
|
56
76
|
if (!manifest) {
|
|
57
77
|
return;
|
|
58
78
|
}
|
|
59
|
-
|
|
79
|
+
// Only update version if package version is >= manifest version
|
|
80
|
+
// This prevents older cached CLI versions from downgrading the manifest
|
|
81
|
+
if (compareVersions(PACKAGE_VERSION, manifest.version) >= 0) {
|
|
82
|
+
manifest.version = PACKAGE_VERSION;
|
|
83
|
+
}
|
|
60
84
|
manifest.updatedAt = new Date().toISOString();
|
|
61
85
|
await writeFile(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
62
86
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version freshness checks for sequant
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities to check if the current version is up to date
|
|
5
|
+
* with the latest version on npm.
|
|
6
|
+
*/
|
|
7
|
+
export interface VersionCache {
|
|
8
|
+
latestVersion: string;
|
|
9
|
+
checkedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export interface VersionCheckResult {
|
|
12
|
+
currentVersion: string;
|
|
13
|
+
latestVersion: string | null;
|
|
14
|
+
isOutdated: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get the global cache directory path
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCacheDir(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Get the version cache file path
|
|
23
|
+
*/
|
|
24
|
+
export declare function getCachePath(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Get the current version from package.json
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCurrentVersion(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Read the version cache
|
|
31
|
+
*/
|
|
32
|
+
export declare function readCache(): VersionCache | null;
|
|
33
|
+
/**
|
|
34
|
+
* Write to the version cache
|
|
35
|
+
*/
|
|
36
|
+
export declare function writeCache(latestVersion: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Check if the cache is still fresh (within 24 hours)
|
|
39
|
+
*/
|
|
40
|
+
export declare function isCacheFresh(cache: VersionCache): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Fetch the latest version from npm registry with timeout
|
|
43
|
+
*/
|
|
44
|
+
export declare function fetchLatestVersion(): Promise<string | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Compare two semantic versions
|
|
47
|
+
* Returns: -1 if a < b, 0 if a == b, 1 if a > b
|
|
48
|
+
*/
|
|
49
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
50
|
+
/**
|
|
51
|
+
* Check if the current version is outdated
|
|
52
|
+
*/
|
|
53
|
+
export declare function isOutdated(currentVersion: string, latestVersion: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Get the version warning message
|
|
56
|
+
*/
|
|
57
|
+
export declare function getVersionWarning(currentVersion: string, latestVersion: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Check version freshness (thorough - for doctor command)
|
|
60
|
+
* Always fetches from npm registry
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkVersionThorough(): Promise<VersionCheckResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Check version freshness (cached - for run command)
|
|
65
|
+
* Uses cache if available and fresh, otherwise fetches (non-blocking)
|
|
66
|
+
*/
|
|
67
|
+
export declare function checkVersionCached(): Promise<VersionCheckResult>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version freshness checks for sequant
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities to check if the current version is up to date
|
|
5
|
+
* with the latest version on npm.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const PACKAGE_NAME = "sequant";
|
|
13
|
+
const NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
14
|
+
const VERSION_CHECK_TIMEOUT = 3000; // 3 seconds
|
|
15
|
+
const CACHE_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
16
|
+
/**
|
|
17
|
+
* Get the global cache directory path
|
|
18
|
+
*/
|
|
19
|
+
export function getCacheDir() {
|
|
20
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
21
|
+
return path.join(home, ".cache", "sequant");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get the version cache file path
|
|
25
|
+
*/
|
|
26
|
+
export function getCachePath() {
|
|
27
|
+
return path.join(getCacheDir(), "version-check.json");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the current version from package.json
|
|
31
|
+
*/
|
|
32
|
+
export function getCurrentVersion() {
|
|
33
|
+
try {
|
|
34
|
+
// Navigate from dist/lib to package.json
|
|
35
|
+
const packagePath = path.resolve(__dirname, "..", "..", "package.json");
|
|
36
|
+
const content = fs.readFileSync(packagePath, "utf8");
|
|
37
|
+
const pkg = JSON.parse(content);
|
|
38
|
+
return pkg.version || "0.0.0";
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return "0.0.0";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read the version cache
|
|
46
|
+
*/
|
|
47
|
+
export function readCache() {
|
|
48
|
+
try {
|
|
49
|
+
const cachePath = getCachePath();
|
|
50
|
+
if (!fs.existsSync(cachePath)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const content = fs.readFileSync(cachePath, "utf8");
|
|
54
|
+
return JSON.parse(content);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Write to the version cache
|
|
62
|
+
*/
|
|
63
|
+
export function writeCache(latestVersion) {
|
|
64
|
+
try {
|
|
65
|
+
const cacheDir = getCacheDir();
|
|
66
|
+
if (!fs.existsSync(cacheDir)) {
|
|
67
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
const cache = {
|
|
70
|
+
latestVersion,
|
|
71
|
+
checkedAt: new Date().toISOString(),
|
|
72
|
+
};
|
|
73
|
+
fs.writeFileSync(getCachePath(), JSON.stringify(cache, null, 2));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Silent failure - caching is optional
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if the cache is still fresh (within 24 hours)
|
|
81
|
+
*/
|
|
82
|
+
export function isCacheFresh(cache) {
|
|
83
|
+
try {
|
|
84
|
+
const checkedAt = new Date(cache.checkedAt).getTime();
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
return now - checkedAt < CACHE_EXPIRY_MS;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Fetch the latest version from npm registry with timeout
|
|
94
|
+
*/
|
|
95
|
+
export async function fetchLatestVersion() {
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
const timeoutId = setTimeout(() => controller.abort(), VERSION_CHECK_TIMEOUT);
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
100
|
+
signal: controller.signal,
|
|
101
|
+
headers: {
|
|
102
|
+
Accept: "application/json",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
clearTimeout(timeoutId);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const data = (await response.json());
|
|
110
|
+
return data.version || null;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compare two semantic versions
|
|
119
|
+
* Returns: -1 if a < b, 0 if a == b, 1 if a > b
|
|
120
|
+
*/
|
|
121
|
+
export function compareVersions(a, b) {
|
|
122
|
+
const parseVersion = (v) => {
|
|
123
|
+
return v
|
|
124
|
+
.replace(/^v/, "")
|
|
125
|
+
.split(".")
|
|
126
|
+
.map((n) => parseInt(n, 10) || 0);
|
|
127
|
+
};
|
|
128
|
+
const aParts = parseVersion(a);
|
|
129
|
+
const bParts = parseVersion(b);
|
|
130
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
131
|
+
const aVal = aParts[i] || 0;
|
|
132
|
+
const bVal = bParts[i] || 0;
|
|
133
|
+
if (aVal < bVal)
|
|
134
|
+
return -1;
|
|
135
|
+
if (aVal > bVal)
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if the current version is outdated
|
|
142
|
+
*/
|
|
143
|
+
export function isOutdated(currentVersion, latestVersion) {
|
|
144
|
+
return compareVersions(currentVersion, latestVersion) < 0;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the version warning message
|
|
148
|
+
*/
|
|
149
|
+
export function getVersionWarning(currentVersion, latestVersion) {
|
|
150
|
+
return `sequant ${latestVersion} is available (you have ${currentVersion})
|
|
151
|
+
Run: npx sequant@latest or npm update sequant`;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Check version freshness (thorough - for doctor command)
|
|
155
|
+
* Always fetches from npm registry
|
|
156
|
+
*/
|
|
157
|
+
export async function checkVersionThorough() {
|
|
158
|
+
const currentVersion = getCurrentVersion();
|
|
159
|
+
const latestVersion = await fetchLatestVersion();
|
|
160
|
+
if (!latestVersion) {
|
|
161
|
+
return {
|
|
162
|
+
currentVersion,
|
|
163
|
+
latestVersion: null,
|
|
164
|
+
isOutdated: false,
|
|
165
|
+
error: "Could not fetch latest version",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Update cache with fresh data
|
|
169
|
+
writeCache(latestVersion);
|
|
170
|
+
return {
|
|
171
|
+
currentVersion,
|
|
172
|
+
latestVersion,
|
|
173
|
+
isOutdated: isOutdated(currentVersion, latestVersion),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check version freshness (cached - for run command)
|
|
178
|
+
* Uses cache if available and fresh, otherwise fetches (non-blocking)
|
|
179
|
+
*/
|
|
180
|
+
export async function checkVersionCached() {
|
|
181
|
+
const currentVersion = getCurrentVersion();
|
|
182
|
+
// Check cache first
|
|
183
|
+
const cache = readCache();
|
|
184
|
+
if (cache && isCacheFresh(cache)) {
|
|
185
|
+
return {
|
|
186
|
+
currentVersion,
|
|
187
|
+
latestVersion: cache.latestVersion,
|
|
188
|
+
isOutdated: isOutdated(currentVersion, cache.latestVersion),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Fetch new version (with timeout)
|
|
192
|
+
const latestVersion = await fetchLatestVersion();
|
|
193
|
+
if (!latestVersion) {
|
|
194
|
+
// Use stale cache if available, otherwise silent failure
|
|
195
|
+
if (cache) {
|
|
196
|
+
return {
|
|
197
|
+
currentVersion,
|
|
198
|
+
latestVersion: cache.latestVersion,
|
|
199
|
+
isOutdated: isOutdated(currentVersion, cache.latestVersion),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
currentVersion,
|
|
204
|
+
latestVersion: null,
|
|
205
|
+
isOutdated: false,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// Update cache
|
|
209
|
+
writeCache(latestVersion);
|
|
210
|
+
return {
|
|
211
|
+
currentVersion,
|
|
212
|
+
latestVersion,
|
|
213
|
+
isOutdated: isOutdated(currentVersion, latestVersion),
|
|
214
|
+
};
|
|
215
|
+
}
|
package/package.json
CHANGED
|
@@ -114,22 +114,17 @@ If no feature worktree exists (work was done directly on main):
|
|
|
114
114
|
|
|
115
115
|
4. **Run quality checks** on the current branch instead of comparing to a worktree.
|
|
116
116
|
|
|
117
|
-
###
|
|
117
|
+
### Quality Checks (Multi-Agent) ā REQUIRED
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
**You MUST spawn sub-agents for quality checks.** Do NOT run these checks inline with bash commands. Sub-agents provide parallel execution, better context isolation, and consistent reporting.
|
|
120
120
|
|
|
121
|
-
**Spawn agents in a SINGLE message:**
|
|
121
|
+
**Spawn ALL THREE agents in a SINGLE message:**
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
Task(subagent_type="quality-checker", model="haiku",
|
|
125
|
-
prompt="Run type safety and deleted tests checks. Report: type issues count, deleted tests, verdict.")
|
|
123
|
+
1. `Task(subagent_type="quality-checker", model="haiku", prompt="Run type safety and deleted tests checks on the current branch vs main. Report: type issues count, deleted tests, verdict.")`
|
|
126
124
|
|
|
127
|
-
Task(subagent_type="quality-checker", model="haiku",
|
|
128
|
-
prompt="Run scope and size checks. Report: files count, diff size, size assessment.")
|
|
125
|
+
2. `Task(subagent_type="quality-checker", model="haiku", prompt="Run scope and size checks on the current branch vs main. Report: files count, diff size, size assessment.")`
|
|
129
126
|
|
|
130
|
-
Task(subagent_type="quality-checker", model="haiku",
|
|
131
|
-
prompt="Run security scan on changed files. Report: critical/warning/info counts, verdict.")
|
|
132
|
-
```
|
|
127
|
+
3. `Task(subagent_type="quality-checker", model="haiku", prompt="Run security scan on changed files in current branch vs main. Report: critical/warning/info counts, verdict.")`
|
|
133
128
|
|
|
134
129
|
**Add RLS check if admin files modified:**
|
|
135
130
|
```bash
|
|
@@ -191,9 +186,9 @@ Provide an overall verdict:
|
|
|
191
186
|
|
|
192
187
|
See [quality-gates.md](references/quality-gates.md) for detailed verdict criteria.
|
|
193
188
|
|
|
194
|
-
## Automated Quality Checks
|
|
189
|
+
## Automated Quality Checks (Reference)
|
|
195
190
|
|
|
196
|
-
|
|
191
|
+
**Note:** These commands are what the sub-agents execute internally. You do NOT run these directly ā the sub-agents spawned above handle this. This section is reference documentation only.
|
|
197
192
|
|
|
198
193
|
```bash
|
|
199
194
|
# Type safety
|
|
@@ -45,22 +45,17 @@ When called like `/spec <freeform description>`:
|
|
|
45
45
|
|
|
46
46
|
**Planning Phase:** No worktree needed. Planning happens in the main repository directory. The worktree will be created during the execution phase (`/exec`).
|
|
47
47
|
|
|
48
|
-
### Parallel Context Gathering
|
|
48
|
+
### Parallel Context Gathering ā REQUIRED
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
**You MUST spawn sub-agents for context gathering.** Do NOT explore the codebase inline with Glob/Grep commands. Sub-agents provide parallel execution, better context isolation, and consistent reporting.
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
Task(subagent_type="pattern-scout", model="haiku",
|
|
54
|
-
prompt="Find similar features. Check components/admin/, lib/queries/, docs/patterns/. Report: file paths, patterns, recommendations.")
|
|
52
|
+
**Spawn ALL THREE agents in a SINGLE message:**
|
|
55
53
|
|
|
56
|
-
Task(subagent_type="
|
|
57
|
-
prompt="Explore [CODEBASE AREA]. Find: main components, data flow, key files. Report structure.")
|
|
54
|
+
1. `Task(subagent_type="pattern-scout", model="haiku", prompt="Find similar features for [FEATURE]. Check components/admin/, lib/queries/, docs/patterns/. Report: file paths, patterns, recommendations.")`
|
|
58
55
|
|
|
59
|
-
Task(subagent_type="
|
|
60
|
-
prompt="Inspect database for [FEATURE]. Check: table schema, RLS policies, existing queries. Report findings.")
|
|
61
|
-
```
|
|
56
|
+
2. `Task(subagent_type="Explore", model="haiku", prompt="Explore [CODEBASE AREA] for [FEATURE]. Find: main components, data flow, key files. Report structure.")`
|
|
62
57
|
|
|
63
|
-
|
|
58
|
+
3. `Task(subagent_type="schema-inspector", model="haiku", prompt="Inspect database for [FEATURE]. Check: table schema, RLS policies, existing queries. Report findings.")`
|
|
64
59
|
|
|
65
60
|
### In-Flight Work Analysis (Conflict Detection)
|
|
66
61
|
|