specweave 1.0.253 ā 1.0.254
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/CLAUDE.md +24 -24
- package/README.md +161 -0
- package/dist/src/cli/commands/docs.d.ts.map +1 -1
- package/dist/src/cli/commands/docs.js +59 -55
- package/dist/src/cli/commands/docs.js.map +1 -1
- package/dist/src/cli/helpers/init/smart-defaults.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/smart-defaults.js +7 -1
- package/dist/src/cli/helpers/init/smart-defaults.js.map +1 -1
- package/dist/src/core/config/types.d.ts +2 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -1
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +5 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +21 -0
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.js +61 -107
- package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
- package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/enterprise/history-analyzer.js +1 -3
- package/dist/src/core/living-docs/enterprise/history-analyzer.js.map +1 -1
- package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -1
- package/dist/src/core/living-docs/governance/java-standards-parser.js +37 -18
- package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -1
- package/dist/src/core/living-docs/operations/ops-generator.js +2 -2
- package/dist/src/core/living-docs/operations/ops-generator.js.map +1 -1
- package/dist/src/core/repo-structure/prefix-validator.d.ts +25 -0
- package/dist/src/core/repo-structure/prefix-validator.d.ts.map +1 -0
- package/dist/src/core/repo-structure/prefix-validator.js +47 -0
- package/dist/src/core/repo-structure/prefix-validator.js.map +1 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts +4 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/repo-structure/role-prefix-collector.d.ts +45 -0
- package/dist/src/core/repo-structure/role-prefix-collector.d.ts.map +1 -0
- package/dist/src/core/repo-structure/role-prefix-collector.js +106 -0
- package/dist/src/core/repo-structure/role-prefix-collector.js.map +1 -0
- package/dist/src/hooks/session-start.js +7 -0
- package/dist/src/hooks/session-start.js.map +1 -1
- package/dist/src/sync/story-router.d.ts +36 -0
- package/dist/src/sync/story-router.d.ts.map +1 -0
- package/dist/src/sync/story-router.js +56 -0
- package/dist/src/sync/story-router.js.map +1 -0
- package/dist/src/utils/state-cleanup.d.ts +19 -0
- package/dist/src/utils/state-cleanup.d.ts.map +1 -0
- package/dist/src/utils/state-cleanup.js +61 -0
- package/dist/src/utils/state-cleanup.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/user-prompt-submit.sh +29 -7
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role & Prefix Collector
|
|
3
|
+
*
|
|
4
|
+
* Interactive flow that runs AFTER repo discovery (manual, bulk, or greenfield).
|
|
5
|
+
* Asks the user to assign a role and story prefix to each repository,
|
|
6
|
+
* then optionally designate a parent repo for centralized tracking.
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { select, input, confirm } from '@inquirer/prompts';
|
|
10
|
+
import { validatePrefix, ROLE_PREFIX_DEFAULTS } from './prefix-validator.js';
|
|
11
|
+
const ROLE_CHOICES = [
|
|
12
|
+
{ name: 'Frontend (web, mobile UI)', value: 'frontend' },
|
|
13
|
+
{ name: 'Backend (API, services)', value: 'backend' },
|
|
14
|
+
{ name: 'Mobile (iOS, Android app)', value: 'mobile' },
|
|
15
|
+
{ name: 'Infrastructure (Terraform, K8s)', value: 'infra' },
|
|
16
|
+
{ name: 'Shared (libraries, utilities)', value: 'shared' },
|
|
17
|
+
{ name: 'Other', value: 'other' },
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Collect role and story prefix for each discovered repo.
|
|
21
|
+
*
|
|
22
|
+
* Runs AFTER repos are discovered/configured. Asks per-repo:
|
|
23
|
+
* 1. What is this repo for? (role)
|
|
24
|
+
* 2. Story prefix? (with role-based default, validated for uniqueness)
|
|
25
|
+
*/
|
|
26
|
+
export async function collectRolesAndPrefixes(repos) {
|
|
27
|
+
if (repos.length === 0)
|
|
28
|
+
return [];
|
|
29
|
+
console.log(chalk.cyan('\nš·ļø Repository Roles & Prefixes\n'));
|
|
30
|
+
console.log(chalk.gray('Each repo gets a role and a prefix for user stories (e.g., US-FE-001).\n'));
|
|
31
|
+
const result = [];
|
|
32
|
+
const usedPrefixes = new Set();
|
|
33
|
+
for (const repo of repos) {
|
|
34
|
+
console.log(chalk.white(`\n${repo.name}:`));
|
|
35
|
+
const role = await select({
|
|
36
|
+
message: 'What is this repository for?',
|
|
37
|
+
choices: ROLE_CHOICES,
|
|
38
|
+
});
|
|
39
|
+
const defaultPrefix = ROLE_PREFIX_DEFAULTS[role] || 'MISC';
|
|
40
|
+
const prefix = await input({
|
|
41
|
+
message: 'Story prefix (e.g., US-FE-001):',
|
|
42
|
+
default: defaultPrefix,
|
|
43
|
+
validate: (val) => {
|
|
44
|
+
const validation = validatePrefix(val, usedPrefixes);
|
|
45
|
+
if (!validation.valid)
|
|
46
|
+
return validation.error;
|
|
47
|
+
return true;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// Normalize and track
|
|
51
|
+
const validation = validatePrefix(prefix, usedPrefixes);
|
|
52
|
+
const normalized = validation.normalized || prefix.toUpperCase();
|
|
53
|
+
usedPrefixes.add(normalized);
|
|
54
|
+
console.log(chalk.green(` ā ${repo.name} ā ${normalized} stories\n`));
|
|
55
|
+
result.push({
|
|
56
|
+
...repo,
|
|
57
|
+
role,
|
|
58
|
+
storyPrefix: normalized,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Ask if user wants a parent repo for centralized issue tracking.
|
|
65
|
+
*
|
|
66
|
+
* Only offered when 2+ repos exist. Shows clear explanation of what
|
|
67
|
+
* a parent repo means and when to use it.
|
|
68
|
+
*
|
|
69
|
+
* @returns Selected parent repo ID, or undefined if declined
|
|
70
|
+
*/
|
|
71
|
+
export async function promptParentRepo(repos) {
|
|
72
|
+
if (repos.length < 2)
|
|
73
|
+
return undefined;
|
|
74
|
+
console.log(chalk.cyan('\nš¦ Issue Tracking Strategy\n'));
|
|
75
|
+
const wantParent = await confirm({
|
|
76
|
+
message: [
|
|
77
|
+
'Do you want a parent/coordination repository?\n',
|
|
78
|
+
chalk.gray(' A parent repo tracks ALL work items in one place (issues, milestones).'),
|
|
79
|
+
chalk.gray(' It typically contains only docs/specs, no application code.\n'),
|
|
80
|
+
chalk.gray(' Use a parent repo if:'),
|
|
81
|
+
chalk.gray(' ā You have 3+ microservices and want one place for all work'),
|
|
82
|
+
chalk.gray(' ā Your team uses a central "specs" or "planning" repo\n'),
|
|
83
|
+
chalk.gray(' Skip parent repo if:'),
|
|
84
|
+
chalk.gray(' ā You only have 1-2 repos (just use the main repo for issues)'),
|
|
85
|
+
chalk.gray(' ā Each service has its own PM/team\n'),
|
|
86
|
+
chalk.gray(' Examples:'),
|
|
87
|
+
chalk.gray(' ⢠WITH parent: acme-specs (tracks), acme-frontend, acme-api (code)'),
|
|
88
|
+
chalk.gray(' ⢠WITHOUT parent: my-frontend (tracks + code), my-api (tracks + code)\n'),
|
|
89
|
+
].join('\n'),
|
|
90
|
+
default: false,
|
|
91
|
+
});
|
|
92
|
+
if (!wantParent) {
|
|
93
|
+
console.log(chalk.gray(' ā First repository will be default for issues\n'));
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const parentId = await select({
|
|
97
|
+
message: 'Which repo should be the parent for issue tracking?',
|
|
98
|
+
choices: repos.map(r => ({
|
|
99
|
+
name: r.name,
|
|
100
|
+
value: r.id,
|
|
101
|
+
})),
|
|
102
|
+
});
|
|
103
|
+
console.log(chalk.green(`\n ā Using "${parentId}" as parent for all work items\n`));
|
|
104
|
+
return parentId;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=role-prefix-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-prefix-collector.js","sourceRoot":"","sources":["../../../../src/core/repo-structure/role-prefix-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ import * as path from 'path';
|
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
18
|
import { findProjectRoot, spawnNodeBackground, outputHookResult, consumeStdin, appendLog, } from './platform.js';
|
|
19
|
+
import { cleanOrphanedStateFiles } from '../utils/state-cleanup.js';
|
|
19
20
|
// Proper cross-platform URL-to-path conversion
|
|
20
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
22
|
const __dirname = path.dirname(__filename);
|
|
@@ -43,6 +44,12 @@ async function main() {
|
|
|
43
44
|
// Consume stdin (required by hook protocol)
|
|
44
45
|
await consumeStdin();
|
|
45
46
|
const logFile = path.join(projectRoot, '.specweave', 'logs', 'session-start.log');
|
|
47
|
+
// v1.0.254: Clean up orphaned state files on session start
|
|
48
|
+
const stateDir = path.join(projectRoot, '.specweave', 'state');
|
|
49
|
+
const cleaned = cleanOrphanedStateFiles(stateDir);
|
|
50
|
+
if (cleaned > 0) {
|
|
51
|
+
appendLog(logFile, `Cleaned ${cleaned} orphaned state files`);
|
|
52
|
+
}
|
|
46
53
|
const distHooksDir = path.join(projectRoot, 'node_modules', 'specweave', 'dist', 'hooks');
|
|
47
54
|
// Check for processor script
|
|
48
55
|
// First try: built hooks in dist (production)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../../src/hooks/session-start.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,SAAS,GACV,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../../src/hooks/session-start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story Router
|
|
3
|
+
*
|
|
4
|
+
* Routes user stories to repos by prefix matching.
|
|
5
|
+
* Format: US-{PREFIX}-{NUM} ā matches repo with that prefix.
|
|
6
|
+
* Falls back to first repo (or custom default) when no match.
|
|
7
|
+
*/
|
|
8
|
+
import type { ChildRepoConfig } from '../core/config/types.js';
|
|
9
|
+
export interface StoryRoutingResult {
|
|
10
|
+
/** Matched repo ID, or null if no repos available */
|
|
11
|
+
repoId: string | null;
|
|
12
|
+
/** Whether routing was an explicit prefix match */
|
|
13
|
+
matched: boolean;
|
|
14
|
+
/** Reason for routing decision (useful for debugging) */
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract prefix from a user story ID.
|
|
19
|
+
*
|
|
20
|
+
* Supports: US-FE-001, US-SHARED-042, us-be-007 (case-insensitive)
|
|
21
|
+
* Returns null for: US-001 (no prefix), STORY-123 (wrong format)
|
|
22
|
+
*
|
|
23
|
+
* @param storyId - The story identifier (e.g., "US-FE-001")
|
|
24
|
+
* @returns Extracted prefix in uppercase, or null
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractPrefix(storyId: string): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Route a user story to a repo by prefix matching.
|
|
29
|
+
*
|
|
30
|
+
* @param storyId - Story identifier (e.g., "US-FE-001")
|
|
31
|
+
* @param childRepos - Available repos with prefix config
|
|
32
|
+
* @param defaultRepoId - Optional explicit default repo ID
|
|
33
|
+
* @returns Routing result with repoId, matched flag, and reason
|
|
34
|
+
*/
|
|
35
|
+
export declare function routeByPrefix(storyId: string, childRepos: ChildRepoConfig[], defaultRepoId?: string): StoryRoutingResult;
|
|
36
|
+
//# sourceMappingURL=story-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-router.d.ts","sourceRoot":"","sources":["../../../src/sync/story-router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ5D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,eAAe,EAAE,EAC7B,aAAa,CAAC,EAAE,MAAM,GACrB,kBAAkB,CA0BpB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story Router
|
|
3
|
+
*
|
|
4
|
+
* Routes user stories to repos by prefix matching.
|
|
5
|
+
* Format: US-{PREFIX}-{NUM} ā matches repo with that prefix.
|
|
6
|
+
* Falls back to first repo (or custom default) when no match.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Extract prefix from a user story ID.
|
|
10
|
+
*
|
|
11
|
+
* Supports: US-FE-001, US-SHARED-042, us-be-007 (case-insensitive)
|
|
12
|
+
* Returns null for: US-001 (no prefix), STORY-123 (wrong format)
|
|
13
|
+
*
|
|
14
|
+
* @param storyId - The story identifier (e.g., "US-FE-001")
|
|
15
|
+
* @returns Extracted prefix in uppercase, or null
|
|
16
|
+
*/
|
|
17
|
+
export function extractPrefix(storyId) {
|
|
18
|
+
if (!storyId)
|
|
19
|
+
return null;
|
|
20
|
+
// Match US-{PREFIX}-{NUM} where PREFIX is 2-6 letters
|
|
21
|
+
const match = storyId.match(/^US-([A-Za-z]{2,6})-\d+/i);
|
|
22
|
+
if (!match)
|
|
23
|
+
return null;
|
|
24
|
+
return match[1].toUpperCase();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Route a user story to a repo by prefix matching.
|
|
28
|
+
*
|
|
29
|
+
* @param storyId - Story identifier (e.g., "US-FE-001")
|
|
30
|
+
* @param childRepos - Available repos with prefix config
|
|
31
|
+
* @param defaultRepoId - Optional explicit default repo ID
|
|
32
|
+
* @returns Routing result with repoId, matched flag, and reason
|
|
33
|
+
*/
|
|
34
|
+
export function routeByPrefix(storyId, childRepos, defaultRepoId) {
|
|
35
|
+
if (childRepos.length === 0) {
|
|
36
|
+
return { repoId: null, matched: false, reason: 'No repos configured' };
|
|
37
|
+
}
|
|
38
|
+
const fallbackId = defaultRepoId
|
|
39
|
+
? (childRepos.find(r => r.id === defaultRepoId)?.id ?? childRepos[0].id)
|
|
40
|
+
: childRepos[0].id;
|
|
41
|
+
const prefix = extractPrefix(storyId);
|
|
42
|
+
if (!prefix) {
|
|
43
|
+
return { repoId: fallbackId, matched: false, reason: 'No prefix in story ID' };
|
|
44
|
+
}
|
|
45
|
+
// Try exact prefix match
|
|
46
|
+
const matched = childRepos.find(r => r.prefix && r.prefix.toUpperCase() === prefix);
|
|
47
|
+
if (matched) {
|
|
48
|
+
return { repoId: matched.id, matched: true, reason: `Matched prefix ${prefix}` };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
repoId: fallbackId,
|
|
52
|
+
matched: false,
|
|
53
|
+
reason: `Unknown prefix "${prefix}" ā using default`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=story-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-router.js","sourceRoot":"","sources":["../../../src/sync/story-router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,sDAAsD;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,UAA6B,EAC7B,aAAsB;IAEtB,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACzE,CAAC;IAED,MAAM,UAAU,GAAG,aAAa;QAC9B,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,aAAa,CAAC,EAAE,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAErB,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IACjF,CAAC;IAED,yBAAyB;IACzB,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IACpF,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,MAAM,EAAE,EAAE,CAAC;IACnF,CAAC;IAED,OAAO;QACL,MAAM,EAAE,UAAU;QAClB,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,mBAAmB,MAAM,mBAAmB;KACrD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State directory cleanup utility (v1.0.254)
|
|
3
|
+
*
|
|
4
|
+
* Removes orphaned marker files from .specweave/state/ that accumulate
|
|
5
|
+
* over time and are never cleaned up. These don't cause functional issues
|
|
6
|
+
* but contribute to state directory bloat.
|
|
7
|
+
*
|
|
8
|
+
* Called from session-start hook to run once per session.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/state-cleanup
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Remove orphaned state marker files older than 7 days.
|
|
14
|
+
*
|
|
15
|
+
* @param stateDir - Path to .specweave/state/ directory
|
|
16
|
+
* @returns Number of files removed
|
|
17
|
+
*/
|
|
18
|
+
export declare function cleanOrphanedStateFiles(stateDir: string): number;
|
|
19
|
+
//# sourceMappingURL=state-cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-cleanup.d.ts","sourceRoot":"","sources":["../../../src/utils/state-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgChE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State directory cleanup utility (v1.0.254)
|
|
3
|
+
*
|
|
4
|
+
* Removes orphaned marker files from .specweave/state/ that accumulate
|
|
5
|
+
* over time and are never cleaned up. These don't cause functional issues
|
|
6
|
+
* but contribute to state directory bloat.
|
|
7
|
+
*
|
|
8
|
+
* Called from session-start hook to run once per session.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/state-cleanup
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
/** Patterns of orphaned marker files to clean up */
|
|
15
|
+
const ORPHAN_PATTERNS = [
|
|
16
|
+
/^\.prev-status-/,
|
|
17
|
+
/^\.github-sync-/,
|
|
18
|
+
/^\.living-specs-/,
|
|
19
|
+
];
|
|
20
|
+
/** Files older than this (in ms) are eligible for cleanup */
|
|
21
|
+
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
22
|
+
/**
|
|
23
|
+
* Remove orphaned state marker files older than 7 days.
|
|
24
|
+
*
|
|
25
|
+
* @param stateDir - Path to .specweave/state/ directory
|
|
26
|
+
* @returns Number of files removed
|
|
27
|
+
*/
|
|
28
|
+
export function cleanOrphanedStateFiles(stateDir) {
|
|
29
|
+
if (!fs.existsSync(stateDir))
|
|
30
|
+
return 0;
|
|
31
|
+
let removed = 0;
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
try {
|
|
34
|
+
const entries = fs.readdirSync(stateDir);
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
// Check if filename matches any orphan pattern
|
|
37
|
+
const isOrphan = ORPHAN_PATTERNS.some(pattern => pattern.test(entry));
|
|
38
|
+
if (!isOrphan)
|
|
39
|
+
continue;
|
|
40
|
+
const filePath = path.join(stateDir, entry);
|
|
41
|
+
try {
|
|
42
|
+
const stat = fs.statSync(filePath);
|
|
43
|
+
if (!stat.isFile())
|
|
44
|
+
continue;
|
|
45
|
+
const age = now - stat.mtimeMs;
|
|
46
|
+
if (age > MAX_AGE_MS) {
|
|
47
|
+
fs.unlinkSync(filePath);
|
|
48
|
+
removed++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Skip files we can't stat or delete
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Directory read failed ā not critical
|
|
58
|
+
}
|
|
59
|
+
return removed;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=state-cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-cleanup.js","sourceRoot":"","sources":["../../../src/utils/state-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,oDAAoD;AACpD,MAAM,eAAe,GAAG;IACtB,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;CACnB,CAAC;AAEF,6DAA6D;AAC7D,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AAErD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,CAAC;IAEvC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACzC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,+CAA+C;YAC/C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;oBAAE,SAAS;gBAE7B,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;gBAC/B,IAAI,GAAG,GAAG,UAAU,EAAE,CAAC;oBACrB,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;oBACxB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qCAAqC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.254",
|
|
4
4
|
"description": "Spec-driven development framework for AI coding agents. First-class support for Claude Code ā compatible with any LLM-powered coding tool. Living documentation, autonomous execution, quality gates, and multilingual support (9 languages).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -371,17 +371,40 @@ escape_json_early() {
|
|
|
371
371
|
fi
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
# v1.0.254: Prompt safety limits to prevent "Prompt is too long" errors
|
|
375
|
+
# These must match the constants in src/core/lazy-loading/llm-plugin-detector.ts
|
|
376
|
+
MAX_ADDITIONAL_CONTEXT_LENGTH=8000
|
|
377
|
+
MAX_SKILL_FIRST_PROMPT_LENGTH=2000
|
|
378
|
+
|
|
374
379
|
# Helper: Output approve response with context (Claude Code hook format v1.0.166)
|
|
375
380
|
# CRITICAL: systemMessage is NOT a valid field for UserPromptSubmit hooks!
|
|
376
381
|
# Use hookSpecificOutput.additionalContext instead.
|
|
377
382
|
# See: https://docs.claude.com/en/docs/claude-code/hooks
|
|
383
|
+
#
|
|
384
|
+
# v1.0.254: Added size guard ā truncates additionalContext if it exceeds
|
|
385
|
+
# MAX_ADDITIONAL_CONTEXT_LENGTH to prevent "Prompt is too long" errors.
|
|
378
386
|
output_approve_with_context() {
|
|
379
387
|
local context="$1"
|
|
388
|
+
# v1.0.254: Safety truncation to prevent prompt overflow
|
|
389
|
+
if [[ ${#context} -gt $MAX_ADDITIONAL_CONTEXT_LENGTH ]]; then
|
|
390
|
+
context="${context:0:$MAX_ADDITIONAL_CONTEXT_LENGTH}... [context truncated for safety]"
|
|
391
|
+
fi
|
|
380
392
|
local escaped
|
|
381
393
|
escaped=$(escape_json_early "$context")
|
|
382
394
|
printf '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"%s"}}\n' "$escaped"
|
|
383
395
|
}
|
|
384
396
|
|
|
397
|
+
# Helper: Truncate and escape a prompt for SKILL FIRST args (v1.0.254)
|
|
398
|
+
# Truncates to MAX_SKILL_FIRST_PROMPT_LENGTH, escapes for JSON embedding.
|
|
399
|
+
# Args: $1=raw prompt text
|
|
400
|
+
# Returns: Escaped, truncated prompt on stdout
|
|
401
|
+
truncate_and_escape_prompt() {
|
|
402
|
+
local prompt="$1"
|
|
403
|
+
local truncated="${prompt:0:$MAX_SKILL_FIRST_PROMPT_LENGTH}"
|
|
404
|
+
[[ ${#prompt} -gt $MAX_SKILL_FIRST_PROMPT_LENGTH ]] && truncated="${truncated}... [truncated - see original prompt above]"
|
|
405
|
+
printf '%s' "$truncated" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' '
|
|
406
|
+
}
|
|
407
|
+
|
|
385
408
|
# Helper: Get skill memory content for injection (v1.0.198)
|
|
386
409
|
# Reads .specweave/skill-memories/{skill}.md and extracts learnings.
|
|
387
410
|
# Args: $1=full skill name (e.g., "sw:pm", "sw-frontend:frontend-architect")
|
|
@@ -1386,12 +1409,11 @@ Task({
|
|
|
1386
1409
|
[[ -n "$INC_NAME" ]] && CMD="/sw:increment \"$INC_NAME\""
|
|
1387
1410
|
|
|
1388
1411
|
# v1.0.169: Call sw:increment-planner DIRECTLY (not wrapper)
|
|
1389
|
-
# Pass
|
|
1412
|
+
# Pass user prompt so skill can extract context
|
|
1390
1413
|
# INC_MANDATORY comes from detect-intent LLM response
|
|
1391
1414
|
if [[ "$INC_MANDATORY" == "true" ]]; then
|
|
1392
|
-
#
|
|
1393
|
-
|
|
1394
|
-
ESCAPED_PROMPT=$(printf '%s' "$PROMPT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ')
|
|
1415
|
+
# v1.0.254: Truncate + escape to prevent "Prompt is too long"
|
|
1416
|
+
ESCAPED_PROMPT=$(truncate_and_escape_prompt "$PROMPT")
|
|
1395
1417
|
|
|
1396
1418
|
# v1.0.243: Smart interview gate ā LLM assesses prompt completeness
|
|
1397
1419
|
# before blindly calling increment-planner. If details are missing,
|
|
@@ -1466,7 +1488,7 @@ See CLAUDE.md section \"MANDATORY: Skill Chaining\" for full pattern."
|
|
|
1466
1488
|
exit 0
|
|
1467
1489
|
else
|
|
1468
1490
|
# v1.0.169: Also suggest direct skill call for non-mandatory
|
|
1469
|
-
ESCAPED_PROMPT_SUGGEST=$(
|
|
1491
|
+
ESCAPED_PROMPT_SUGGEST=$(truncate_and_escape_prompt "$PROMPT")
|
|
1470
1492
|
MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}š” **Increment Suggestion**: This looks like new feature work.
|
|
1471
1493
|
|
|
1472
1494
|
Consider creating an increment first:
|
|
@@ -1488,7 +1510,7 @@ Or via command: \`$CMD\`
|
|
|
1488
1510
|
|
|
1489
1511
|
hotfix)
|
|
1490
1512
|
# v1.0.169: Direct skill call for hotfix too
|
|
1491
|
-
ESCAPED_PROMPT_HOTFIX=$(
|
|
1513
|
+
ESCAPED_PROMPT_HOTFIX=$(truncate_and_escape_prompt "$PROMPT")
|
|
1492
1514
|
MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}šØ **Hotfix Detected**: Urgent production issue.
|
|
1493
1515
|
|
|
1494
1516
|
Create a hotfix increment:
|
|
@@ -1530,7 +1552,7 @@ specweave resume <id> # Reopen it
|
|
|
1530
1552
|
small_fix)
|
|
1531
1553
|
# v1.0.241: small_fix still suggests increment (non-mandatory)
|
|
1532
1554
|
# Previously small_fix fell through with no output at all
|
|
1533
|
-
ESCAPED_PROMPT_SMALLFIX=$(
|
|
1555
|
+
ESCAPED_PROMPT_SMALLFIX=$(truncate_and_escape_prompt "$PROMPT")
|
|
1534
1556
|
CMD_SMALLFIX="/sw:increment"
|
|
1535
1557
|
[[ -n "$INC_NAME" ]] && CMD_SMALLFIX="/sw:increment \"$INC_NAME\""
|
|
1536
1558
|
|