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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack configuration persistence
|
|
3
|
+
*
|
|
4
|
+
* Manages user's stack configuration in .sequant/stack.json
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Stack entry with optional path for subdirectory stacks
|
|
8
|
+
*/
|
|
9
|
+
export interface StackEntry {
|
|
10
|
+
name: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Persisted stack configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface StackConfigFile {
|
|
17
|
+
/** Primary stack (determines dev URL and main commands) */
|
|
18
|
+
primary: StackEntry;
|
|
19
|
+
/** Additional stacks to include in constitution notes */
|
|
20
|
+
additional?: StackEntry[];
|
|
21
|
+
/** When this configuration was last updated */
|
|
22
|
+
lastUpdated?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load stack configuration from .sequant/stack.json
|
|
26
|
+
*
|
|
27
|
+
* @returns Stack configuration or null if not found
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadStackConfig(): Promise<StackConfigFile | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Save stack configuration to .sequant/stack.json
|
|
32
|
+
*
|
|
33
|
+
* @param config - Stack configuration to save
|
|
34
|
+
*/
|
|
35
|
+
export declare function saveStackConfig(config: StackConfigFile): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if stack configuration exists
|
|
38
|
+
*/
|
|
39
|
+
export declare function hasStackConfig(): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Get the primary stack name from configuration
|
|
42
|
+
*
|
|
43
|
+
* @returns Primary stack name or null if not configured
|
|
44
|
+
*/
|
|
45
|
+
export declare function getPrimaryStack(): Promise<string | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Get all configured stacks (primary + additional)
|
|
48
|
+
*
|
|
49
|
+
* @returns Array of all stack names
|
|
50
|
+
*/
|
|
51
|
+
export declare function getAllConfiguredStacks(): Promise<string[]>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack configuration persistence
|
|
3
|
+
*
|
|
4
|
+
* Manages user's stack configuration in .sequant/stack.json
|
|
5
|
+
*/
|
|
6
|
+
import { fileExists, readFile, writeFile, ensureDir } from "./fs.js";
|
|
7
|
+
import { dirname } from "path";
|
|
8
|
+
const STACK_CONFIG_PATH = ".sequant/stack.json";
|
|
9
|
+
/**
|
|
10
|
+
* Load stack configuration from .sequant/stack.json
|
|
11
|
+
*
|
|
12
|
+
* @returns Stack configuration or null if not found
|
|
13
|
+
*/
|
|
14
|
+
export async function loadStackConfig() {
|
|
15
|
+
try {
|
|
16
|
+
if (!(await fileExists(STACK_CONFIG_PATH))) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const content = await readFile(STACK_CONFIG_PATH);
|
|
20
|
+
const config = JSON.parse(content);
|
|
21
|
+
// Validate required fields
|
|
22
|
+
if (!config.primary || !config.primary.name) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Save stack configuration to .sequant/stack.json
|
|
33
|
+
*
|
|
34
|
+
* @param config - Stack configuration to save
|
|
35
|
+
*/
|
|
36
|
+
export async function saveStackConfig(config) {
|
|
37
|
+
const configWithTimestamp = {
|
|
38
|
+
...config,
|
|
39
|
+
lastUpdated: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
await ensureDir(dirname(STACK_CONFIG_PATH));
|
|
42
|
+
await writeFile(STACK_CONFIG_PATH, JSON.stringify(configWithTimestamp, null, 2) + "\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if stack configuration exists
|
|
46
|
+
*/
|
|
47
|
+
export async function hasStackConfig() {
|
|
48
|
+
return fileExists(STACK_CONFIG_PATH);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the primary stack name from configuration
|
|
52
|
+
*
|
|
53
|
+
* @returns Primary stack name or null if not configured
|
|
54
|
+
*/
|
|
55
|
+
export async function getPrimaryStack() {
|
|
56
|
+
const config = await loadStackConfig();
|
|
57
|
+
return config?.primary?.name ?? null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get all configured stacks (primary + additional)
|
|
61
|
+
*
|
|
62
|
+
* @returns Array of all stack names
|
|
63
|
+
*/
|
|
64
|
+
export async function getAllConfiguredStacks() {
|
|
65
|
+
const config = await loadStackConfig();
|
|
66
|
+
if (!config)
|
|
67
|
+
return [];
|
|
68
|
+
const stacks = [config.primary.name];
|
|
69
|
+
if (config.additional) {
|
|
70
|
+
for (const entry of config.additional) {
|
|
71
|
+
if (!stacks.includes(entry.name)) {
|
|
72
|
+
stacks.push(entry.name);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return stacks;
|
|
77
|
+
}
|
package/dist/src/lib/stacks.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stack detection and configuration
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* Detected stack with location information
|
|
6
|
+
*/
|
|
7
|
+
export interface DetectedStack {
|
|
8
|
+
/** Stack name (e.g., "nextjs", "python") */
|
|
9
|
+
stack: string;
|
|
10
|
+
/** Path relative to project root (empty string for root) */
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Stack configuration for persistence in .sequant/stack.json
|
|
15
|
+
*/
|
|
16
|
+
export interface StackConfig_Persisted {
|
|
17
|
+
/** Primary stack for the project (determines dev URL, commands) */
|
|
18
|
+
primary: {
|
|
19
|
+
name: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Additional stacks to include in constitution notes */
|
|
23
|
+
additional?: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
path?: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
4
28
|
/**
|
|
5
29
|
* Supported package managers
|
|
6
30
|
*/
|
|
@@ -46,4 +70,46 @@ export interface StackConfig {
|
|
|
46
70
|
}
|
|
47
71
|
export declare const STACKS: Record<string, StackConfig>;
|
|
48
72
|
export declare function detectStack(): Promise<string | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Detect stack in a specific directory
|
|
75
|
+
* Similar to detectStack but operates on a given path
|
|
76
|
+
*
|
|
77
|
+
* @param basePath - Directory path to check (relative to cwd)
|
|
78
|
+
* @returns Stack name or null if not detected
|
|
79
|
+
*/
|
|
80
|
+
export declare function detectStackInDirectory(basePath: string): Promise<string | null>;
|
|
81
|
+
/**
|
|
82
|
+
* Detect all stacks in the repository
|
|
83
|
+
*
|
|
84
|
+
* Traverses root and immediate subdirectories (1 level deep) to find
|
|
85
|
+
* all stacks present in a monorepo or multi-stack project.
|
|
86
|
+
*
|
|
87
|
+
* @returns Array of detected stacks with their paths
|
|
88
|
+
*/
|
|
89
|
+
export declare function detectAllStacks(): Promise<DetectedStack[]>;
|
|
49
90
|
export declare function getStackConfig(stack: string): StackConfig;
|
|
91
|
+
/**
|
|
92
|
+
* Stack-specific notes for constitution templates
|
|
93
|
+
*
|
|
94
|
+
* These notes are injected into the constitution during setup to provide
|
|
95
|
+
* context-aware guidance for AI-assisted development.
|
|
96
|
+
*/
|
|
97
|
+
export declare const STACK_NOTES: Record<string, string>;
|
|
98
|
+
/**
|
|
99
|
+
* Get stack-specific notes for constitution template
|
|
100
|
+
*
|
|
101
|
+
* @param stack - The stack name (e.g., "nextjs", "python", "rust")
|
|
102
|
+
* @returns The stack-specific notes markdown content
|
|
103
|
+
*/
|
|
104
|
+
export declare function getStackNotes(stack: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* Get combined stack notes for multiple stacks
|
|
107
|
+
*
|
|
108
|
+
* Combines notes from a primary stack and optional additional stacks
|
|
109
|
+
* into a single markdown section for multi-stack projects.
|
|
110
|
+
*
|
|
111
|
+
* @param primary - Primary stack name (determines primary tooling)
|
|
112
|
+
* @param additional - Additional stacks to include notes for
|
|
113
|
+
* @returns Combined stack notes markdown content
|
|
114
|
+
*/
|
|
115
|
+
export declare function getMultiStackNotes(primary: string, additional?: string[]): string;
|
package/dist/src/lib/stacks.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stack detection and configuration
|
|
3
3
|
*/
|
|
4
|
+
import { readdir } from "fs/promises";
|
|
4
5
|
import { fileExists, readFile } from "./fs.js";
|
|
6
|
+
/**
|
|
7
|
+
* Directories to skip during multi-stack detection
|
|
8
|
+
*/
|
|
9
|
+
const SKIP_DIRECTORIES = [
|
|
10
|
+
"node_modules",
|
|
11
|
+
".git",
|
|
12
|
+
"vendor",
|
|
13
|
+
"dist",
|
|
14
|
+
"build",
|
|
15
|
+
".next",
|
|
16
|
+
".nuxt",
|
|
17
|
+
".output",
|
|
18
|
+
"__pycache__",
|
|
19
|
+
"target",
|
|
20
|
+
".claude",
|
|
21
|
+
".sequant",
|
|
22
|
+
];
|
|
5
23
|
/**
|
|
6
24
|
* Package manager configurations
|
|
7
25
|
*/
|
|
@@ -307,6 +325,320 @@ export async function detectStack() {
|
|
|
307
325
|
}
|
|
308
326
|
return null;
|
|
309
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Detect stack in a specific directory
|
|
330
|
+
* Similar to detectStack but operates on a given path
|
|
331
|
+
*
|
|
332
|
+
* @param basePath - Directory path to check (relative to cwd)
|
|
333
|
+
* @returns Stack name or null if not detected
|
|
334
|
+
*/
|
|
335
|
+
export async function detectStackInDirectory(basePath) {
|
|
336
|
+
const pathPrefix = basePath ? `${basePath}/` : "";
|
|
337
|
+
// Check for Next.js config files
|
|
338
|
+
for (const file of STACKS.nextjs.detection.files || []) {
|
|
339
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
340
|
+
return "nextjs";
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Check for Astro config files
|
|
344
|
+
for (const file of STACKS.astro.detection.files || []) {
|
|
345
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
346
|
+
return "astro";
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Check for SvelteKit config files
|
|
350
|
+
for (const file of STACKS.sveltekit.detection.files || []) {
|
|
351
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
352
|
+
return "sveltekit";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Check for Remix config files
|
|
356
|
+
for (const file of STACKS.remix.detection.files || []) {
|
|
357
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
358
|
+
return "remix";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Check for Nuxt config files
|
|
362
|
+
for (const file of STACKS.nuxt.detection.files || []) {
|
|
363
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
364
|
+
return "nuxt";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Check package.json for JS framework dependencies
|
|
368
|
+
const packageJsonPath = `${pathPrefix}package.json`;
|
|
369
|
+
if (await fileExists(packageJsonPath)) {
|
|
370
|
+
try {
|
|
371
|
+
const pkg = JSON.parse(await readFile(packageJsonPath));
|
|
372
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
373
|
+
if (deps.next)
|
|
374
|
+
return "nextjs";
|
|
375
|
+
if (deps.astro)
|
|
376
|
+
return "astro";
|
|
377
|
+
if (deps["@sveltejs/kit"])
|
|
378
|
+
return "sveltekit";
|
|
379
|
+
if (deps["@remix-run/react"])
|
|
380
|
+
return "remix";
|
|
381
|
+
if (deps.nuxt)
|
|
382
|
+
return "nuxt";
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
// Ignore parse errors
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Check for Rust
|
|
389
|
+
if (await fileExists(`${pathPrefix}Cargo.toml`)) {
|
|
390
|
+
return "rust";
|
|
391
|
+
}
|
|
392
|
+
// Check for Go
|
|
393
|
+
if (await fileExists(`${pathPrefix}go.mod`)) {
|
|
394
|
+
return "go";
|
|
395
|
+
}
|
|
396
|
+
// Check for Python
|
|
397
|
+
for (const file of STACKS.python.detection.files || []) {
|
|
398
|
+
if (await fileExists(`${pathPrefix}${file}`)) {
|
|
399
|
+
return "python";
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Detect all stacks in the repository
|
|
406
|
+
*
|
|
407
|
+
* Traverses root and immediate subdirectories (1 level deep) to find
|
|
408
|
+
* all stacks present in a monorepo or multi-stack project.
|
|
409
|
+
*
|
|
410
|
+
* @returns Array of detected stacks with their paths
|
|
411
|
+
*/
|
|
412
|
+
export async function detectAllStacks() {
|
|
413
|
+
const results = [];
|
|
414
|
+
// Check root directory
|
|
415
|
+
const rootStack = await detectStackInDirectory("");
|
|
416
|
+
if (rootStack) {
|
|
417
|
+
results.push({ stack: rootStack, path: "" });
|
|
418
|
+
}
|
|
419
|
+
// Check immediate subdirectories (1 level deep)
|
|
420
|
+
try {
|
|
421
|
+
const entries = await readdir(".", { withFileTypes: true });
|
|
422
|
+
for (const entry of entries) {
|
|
423
|
+
if (!entry.isDirectory())
|
|
424
|
+
continue;
|
|
425
|
+
if (SKIP_DIRECTORIES.includes(entry.name))
|
|
426
|
+
continue;
|
|
427
|
+
if (entry.name.startsWith("."))
|
|
428
|
+
continue; // Skip hidden directories
|
|
429
|
+
const subdirStack = await detectStackInDirectory(entry.name);
|
|
430
|
+
if (subdirStack) {
|
|
431
|
+
// Skip if same stack as root (likely a false positive)
|
|
432
|
+
// e.g., both root and /packages/web detect "nextjs"
|
|
433
|
+
// We want distinct stacks, not duplicates
|
|
434
|
+
const isDuplicate = results.some((r) => r.stack === subdirStack && r.path === "");
|
|
435
|
+
// Only skip if root has the same stack AND this is a common mono-repo pattern
|
|
436
|
+
// (like packages/, apps/, etc.)
|
|
437
|
+
if (!isDuplicate ||
|
|
438
|
+
entry.name === "frontend" ||
|
|
439
|
+
entry.name === "backend") {
|
|
440
|
+
results.push({ stack: subdirStack, path: entry.name });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
// Directory read failed, return what we have
|
|
447
|
+
}
|
|
448
|
+
return results;
|
|
449
|
+
}
|
|
310
450
|
export function getStackConfig(stack) {
|
|
311
451
|
return STACKS[stack] || STACKS.generic;
|
|
312
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Stack-specific notes for constitution templates
|
|
455
|
+
*
|
|
456
|
+
* These notes are injected into the constitution during setup to provide
|
|
457
|
+
* context-aware guidance for AI-assisted development.
|
|
458
|
+
*/
|
|
459
|
+
export const STACK_NOTES = {
|
|
460
|
+
nextjs: `### Next.js
|
|
461
|
+
|
|
462
|
+
**Testing:**
|
|
463
|
+
- Use Jest or Vitest with React Testing Library
|
|
464
|
+
- Place tests in \`__tests__/\` directories or as \`.test.ts(x)\` files
|
|
465
|
+
- Use \`next/jest\` preset for proper Next.js configuration
|
|
466
|
+
- Mock \`next/router\` and \`next/navigation\` in component tests
|
|
467
|
+
|
|
468
|
+
**Linting:**
|
|
469
|
+
- ESLint with \`eslint-config-next\` (included by default)
|
|
470
|
+
- Run \`npm run lint\` or \`next lint\`
|
|
471
|
+
- TypeScript strict mode recommended
|
|
472
|
+
|
|
473
|
+
**Build:**
|
|
474
|
+
- Output to \`.next/\` directory
|
|
475
|
+
- Use \`next build\` for production builds
|
|
476
|
+
- Static exports via \`output: 'export'\` in next.config.js`,
|
|
477
|
+
astro: `### Astro
|
|
478
|
+
|
|
479
|
+
**Testing:**
|
|
480
|
+
- Astro projects may not have test scripts configured by default
|
|
481
|
+
- Consider adding Vitest: \`npm install -D vitest\`
|
|
482
|
+
- Use \`@astrojs/test-utils\` for component testing
|
|
483
|
+
|
|
484
|
+
**Linting:**
|
|
485
|
+
- Consider ESLint with \`eslint-plugin-astro\`
|
|
486
|
+
- Install: \`npm install -D eslint eslint-plugin-astro\`
|
|
487
|
+
|
|
488
|
+
**Build:**
|
|
489
|
+
- Output to \`dist/\` by default
|
|
490
|
+
- Use \`astro build\` for production
|
|
491
|
+
- SSR mode available via adapters`,
|
|
492
|
+
sveltekit: `### SvelteKit
|
|
493
|
+
|
|
494
|
+
**Testing:**
|
|
495
|
+
- Use Vitest with \`@testing-library/svelte\`
|
|
496
|
+
- Playwright for E2E testing (often pre-configured)
|
|
497
|
+
- Place unit tests in \`src/\` alongside components
|
|
498
|
+
|
|
499
|
+
**Linting:**
|
|
500
|
+
- ESLint with \`eslint-plugin-svelte\`
|
|
501
|
+
- Prettier with \`prettier-plugin-svelte\`
|
|
502
|
+
- Run \`npm run lint\` and \`npm run check\`
|
|
503
|
+
|
|
504
|
+
**Build:**
|
|
505
|
+
- Output depends on adapter (node, static, vercel, etc.)
|
|
506
|
+
- Use \`svelte-kit build\` for production
|
|
507
|
+
- Type-check with \`svelte-kit sync && svelte-check\``,
|
|
508
|
+
remix: `### Remix
|
|
509
|
+
|
|
510
|
+
**Testing:**
|
|
511
|
+
- Use Vitest or Jest with React Testing Library
|
|
512
|
+
- Cypress or Playwright for E2E testing
|
|
513
|
+
- Test loaders and actions separately from components
|
|
514
|
+
|
|
515
|
+
**Linting:**
|
|
516
|
+
- ESLint with React and TypeScript plugins
|
|
517
|
+
- Remix doesn't include ESLint config by default
|
|
518
|
+
|
|
519
|
+
**Build:**
|
|
520
|
+
- Output to \`build/\` directory
|
|
521
|
+
- Use \`remix build\` for production
|
|
522
|
+
- Server and client bundles are separate`,
|
|
523
|
+
nuxt: `### Nuxt
|
|
524
|
+
|
|
525
|
+
**Testing:**
|
|
526
|
+
- Use Vitest with \`@nuxt/test-utils\`
|
|
527
|
+
- \`@vue/test-utils\` for component testing
|
|
528
|
+
- Playwright for E2E testing
|
|
529
|
+
|
|
530
|
+
**Linting:**
|
|
531
|
+
- ESLint with \`@nuxt/eslint-config\`
|
|
532
|
+
- Run \`npm run lint\` or \`nuxi lint\`
|
|
533
|
+
|
|
534
|
+
**Build:**
|
|
535
|
+
- Output to \`.output/\` directory
|
|
536
|
+
- Use \`nuxi build\` for production
|
|
537
|
+
- \`nuxi generate\` for static site generation`,
|
|
538
|
+
rust: `### Rust
|
|
539
|
+
|
|
540
|
+
**Testing:**
|
|
541
|
+
- Unit tests: \`#[cfg(test)]\` modules in source files
|
|
542
|
+
- Integration tests: \`tests/\` directory
|
|
543
|
+
- Run \`cargo test\` for all tests
|
|
544
|
+
- Use \`#[should_panic]\` for panic tests
|
|
545
|
+
|
|
546
|
+
**Linting:**
|
|
547
|
+
- \`cargo clippy\` for lints (install: \`rustup component add clippy\`)
|
|
548
|
+
- \`cargo fmt\` for formatting
|
|
549
|
+
- Consider \`cargo deny\` for dependency auditing
|
|
550
|
+
|
|
551
|
+
**Build:**
|
|
552
|
+
- Debug: \`cargo build\`
|
|
553
|
+
- Release: \`cargo build --release\`
|
|
554
|
+
- Output to \`target/debug/\` or \`target/release/\``,
|
|
555
|
+
python: `### Python
|
|
556
|
+
|
|
557
|
+
**Testing:**
|
|
558
|
+
- pytest is the standard test runner
|
|
559
|
+
- Place tests in \`tests/\` directory
|
|
560
|
+
- Use \`pytest-cov\` for coverage reports
|
|
561
|
+
- Fixtures in \`conftest.py\`
|
|
562
|
+
|
|
563
|
+
**Linting:**
|
|
564
|
+
- Ruff for fast linting and formatting: \`ruff check .\` and \`ruff format .\`
|
|
565
|
+
- Alternative: Black + isort + flake8
|
|
566
|
+
- mypy for type checking
|
|
567
|
+
|
|
568
|
+
**Build:**
|
|
569
|
+
- Use virtual environments (venv, poetry, pdm)
|
|
570
|
+
- \`python -m build\` for package builds
|
|
571
|
+
- \`pip install -e .\` for development installs`,
|
|
572
|
+
go: `### Go
|
|
573
|
+
|
|
574
|
+
**Testing:**
|
|
575
|
+
- Tests in \`*_test.go\` files alongside source
|
|
576
|
+
- Run \`go test ./...\` for all packages
|
|
577
|
+
- Use \`-v\` for verbose output, \`-race\` for race detection
|
|
578
|
+
- Table-driven tests are idiomatic
|
|
579
|
+
|
|
580
|
+
**Linting:**
|
|
581
|
+
- \`golangci-lint run\` for comprehensive linting
|
|
582
|
+
- \`go vet\` for basic checks
|
|
583
|
+
- \`gofmt\` or \`goimports\` for formatting
|
|
584
|
+
|
|
585
|
+
**Build:**
|
|
586
|
+
- \`go build ./...\` to compile
|
|
587
|
+
- \`go install\` to build and install
|
|
588
|
+
- Cross-compile with GOOS/GOARCH environment variables`,
|
|
589
|
+
generic: `### General Guidelines
|
|
590
|
+
|
|
591
|
+
**Testing:**
|
|
592
|
+
- Check for test scripts in package.json or equivalent
|
|
593
|
+
- Common patterns: \`npm test\`, \`pytest\`, \`go test\`, \`cargo test\`
|
|
594
|
+
|
|
595
|
+
**Linting:**
|
|
596
|
+
- Check for lint scripts or configuration files
|
|
597
|
+
- Common: ESLint, Prettier, Ruff, golangci-lint, clippy
|
|
598
|
+
|
|
599
|
+
**Build:**
|
|
600
|
+
- Check for build scripts and output directories
|
|
601
|
+
- Document build commands in README`,
|
|
602
|
+
};
|
|
603
|
+
/**
|
|
604
|
+
* Get stack-specific notes for constitution template
|
|
605
|
+
*
|
|
606
|
+
* @param stack - The stack name (e.g., "nextjs", "python", "rust")
|
|
607
|
+
* @returns The stack-specific notes markdown content
|
|
608
|
+
*/
|
|
609
|
+
export function getStackNotes(stack) {
|
|
610
|
+
return STACK_NOTES[stack] || STACK_NOTES.generic;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get combined stack notes for multiple stacks
|
|
614
|
+
*
|
|
615
|
+
* Combines notes from a primary stack and optional additional stacks
|
|
616
|
+
* into a single markdown section for multi-stack projects.
|
|
617
|
+
*
|
|
618
|
+
* @param primary - Primary stack name (determines primary tooling)
|
|
619
|
+
* @param additional - Additional stacks to include notes for
|
|
620
|
+
* @returns Combined stack notes markdown content
|
|
621
|
+
*/
|
|
622
|
+
export function getMultiStackNotes(primary, additional = []) {
|
|
623
|
+
const sections = [];
|
|
624
|
+
// Add primary stack notes (with "Primary" marker)
|
|
625
|
+
const primaryNotes = STACK_NOTES[primary] || STACK_NOTES.generic;
|
|
626
|
+
if (additional.length > 0) {
|
|
627
|
+
// Replace the first line heading to include "(Primary)"
|
|
628
|
+
const modifiedPrimary = primaryNotes.replace(/^### (.+)$/m, "### $1 (Primary)");
|
|
629
|
+
sections.push(modifiedPrimary);
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
sections.push(primaryNotes);
|
|
633
|
+
}
|
|
634
|
+
// Add additional stack notes
|
|
635
|
+
for (const stack of additional) {
|
|
636
|
+
if (stack === primary)
|
|
637
|
+
continue; // Skip if same as primary
|
|
638
|
+
const notes = STACK_NOTES[stack];
|
|
639
|
+
if (notes && notes !== STACK_NOTES.generic) {
|
|
640
|
+
sections.push(notes);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return sections.join("\n\n---\n\n");
|
|
644
|
+
}
|
|
@@ -32,6 +32,8 @@ export interface CopyTemplatesOptions {
|
|
|
32
32
|
noSymlinks?: boolean;
|
|
33
33
|
/** Force replacement of existing files/symlinks */
|
|
34
34
|
force?: boolean;
|
|
35
|
+
/** Additional stacks to include in constitution notes (for multi-stack projects) */
|
|
36
|
+
additionalStacks?: string[];
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Create symlinks for files in a directory, with fallback to copy
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Template management - copy and process templates
|
|
3
3
|
*/
|
|
4
4
|
import { readdir, chmod } from "fs/promises";
|
|
5
|
-
import { join, dirname,
|
|
5
|
+
import { join, dirname, relative, isAbsolute } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { readFile, writeFile, ensureDir, fileExists, isSymlink, createSymlink, removeFileOrSymlink, } from "./fs.js";
|
|
8
|
-
import { getStackConfig } from "./stacks.js";
|
|
8
|
+
import { getStackConfig, getStackNotes, getMultiStackNotes } from "./stacks.js";
|
|
9
9
|
import { isNativeWindows } from "./system.js";
|
|
10
|
+
import { getProjectName } from "./project-name.js";
|
|
10
11
|
// Get the package templates directory
|
|
11
12
|
function getTemplatesDir() {
|
|
12
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -157,11 +158,19 @@ export async function symlinkDir(srcDir, destDir, options = {}) {
|
|
|
157
158
|
export async function copyTemplates(stack, tokens, options = {}) {
|
|
158
159
|
const templatesDir = getTemplatesDir();
|
|
159
160
|
const stackConfig = getStackConfig(stack);
|
|
161
|
+
// Detect project name from available sources (package.json, Cargo.toml, etc.)
|
|
162
|
+
const projectName = await getProjectName();
|
|
163
|
+
// Get stack-specific notes for constitution template
|
|
164
|
+
// Use multi-stack notes if additional stacks are provided
|
|
165
|
+
const stackNotes = options.additionalStacks && options.additionalStacks.length > 0
|
|
166
|
+
? getMultiStackNotes(stack, options.additionalStacks)
|
|
167
|
+
: getStackNotes(stack);
|
|
160
168
|
const variables = {
|
|
161
169
|
...stackConfig.variables,
|
|
162
170
|
...tokens,
|
|
163
|
-
PROJECT_NAME:
|
|
171
|
+
PROJECT_NAME: projectName,
|
|
164
172
|
STACK: stack,
|
|
173
|
+
STACK_NOTES: stackNotes,
|
|
165
174
|
};
|
|
166
175
|
async function copyDir(srcDir, destDir) {
|
|
167
176
|
try {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main assessment module for upstream analysis
|
|
3
|
+
* Coordinates release fetching, analysis, and output generation
|
|
4
|
+
*/
|
|
5
|
+
import type { AssessmentOptions, Baseline, BatchedAssessment, ReleaseData, UpstreamAssessment } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Validate a version string to prevent command injection
|
|
8
|
+
* @throws Error if version format is invalid
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateVersion(version: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Check if gh CLI is available and authenticated
|
|
13
|
+
* @returns Object with availability status and error message if not available
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkGhCliAvailable(): Promise<{
|
|
16
|
+
available: boolean;
|
|
17
|
+
authenticated: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Fetch release data from Claude Code repository
|
|
22
|
+
*/
|
|
23
|
+
export declare function fetchRelease(version?: string): Promise<ReleaseData | null>;
|
|
24
|
+
/**
|
|
25
|
+
* List releases from Claude Code repository
|
|
26
|
+
*/
|
|
27
|
+
export declare function listReleases(limit?: number): Promise<Array<{
|
|
28
|
+
tagName: string;
|
|
29
|
+
publishedAt: string;
|
|
30
|
+
}>>;
|
|
31
|
+
/**
|
|
32
|
+
* Get releases since a specific version
|
|
33
|
+
*/
|
|
34
|
+
export declare function getReleasesSince(sinceVersion: string): Promise<string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Load baseline from file
|
|
37
|
+
*/
|
|
38
|
+
export declare function loadBaseline(path?: string): Promise<Baseline>;
|
|
39
|
+
/**
|
|
40
|
+
* Save baseline to file
|
|
41
|
+
*/
|
|
42
|
+
export declare function saveBaseline(baseline: Baseline, path?: string): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Update baseline with new assessed version
|
|
45
|
+
*/
|
|
46
|
+
export declare function updateBaseline(version: string, path?: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a version has already been assessed
|
|
49
|
+
*/
|
|
50
|
+
export declare function isAlreadyAssessed(version: string): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Save local report
|
|
53
|
+
*/
|
|
54
|
+
export declare function saveLocalReport(assessment: UpstreamAssessment): Promise<string>;
|
|
55
|
+
/**
|
|
56
|
+
* Run a single version assessment
|
|
57
|
+
*/
|
|
58
|
+
export declare function assessVersion(version: string, options?: AssessmentOptions): Promise<UpstreamAssessment | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Run assessment for latest release
|
|
61
|
+
*/
|
|
62
|
+
export declare function assessLatest(options?: AssessmentOptions): Promise<UpstreamAssessment | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Run batched assessment for multiple versions
|
|
65
|
+
*/
|
|
66
|
+
export declare function assessSince(sinceVersion: string, options?: AssessmentOptions): Promise<BatchedAssessment | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Main entry point for upstream assessment
|
|
69
|
+
*/
|
|
70
|
+
export declare function runUpstream(options?: AssessmentOptions): Promise<UpstreamAssessment | BatchedAssessment | null>;
|