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.
Files changed (49) hide show
  1. package/CLAUDE.md +24 -24
  2. package/README.md +161 -0
  3. package/dist/src/cli/commands/docs.d.ts.map +1 -1
  4. package/dist/src/cli/commands/docs.js +59 -55
  5. package/dist/src/cli/commands/docs.js.map +1 -1
  6. package/dist/src/cli/helpers/init/smart-defaults.d.ts.map +1 -1
  7. package/dist/src/cli/helpers/init/smart-defaults.js +7 -1
  8. package/dist/src/cli/helpers/init/smart-defaults.js.map +1 -1
  9. package/dist/src/core/config/types.d.ts +2 -0
  10. package/dist/src/core/config/types.d.ts.map +1 -1
  11. package/dist/src/core/config/types.js.map +1 -1
  12. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -1
  13. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +5 -0
  14. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -1
  15. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +21 -0
  16. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  17. package/dist/src/core/lazy-loading/llm-plugin-detector.js +61 -107
  18. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  19. package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts.map +1 -1
  20. package/dist/src/core/living-docs/enterprise/history-analyzer.js +1 -3
  21. package/dist/src/core/living-docs/enterprise/history-analyzer.js.map +1 -1
  22. package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -1
  23. package/dist/src/core/living-docs/governance/java-standards-parser.js +37 -18
  24. package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -1
  25. package/dist/src/core/living-docs/operations/ops-generator.js +2 -2
  26. package/dist/src/core/living-docs/operations/ops-generator.js.map +1 -1
  27. package/dist/src/core/repo-structure/prefix-validator.d.ts +25 -0
  28. package/dist/src/core/repo-structure/prefix-validator.d.ts.map +1 -0
  29. package/dist/src/core/repo-structure/prefix-validator.js +47 -0
  30. package/dist/src/core/repo-structure/prefix-validator.js.map +1 -0
  31. package/dist/src/core/repo-structure/repo-structure-manager.d.ts +4 -0
  32. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  33. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  34. package/dist/src/core/repo-structure/role-prefix-collector.d.ts +45 -0
  35. package/dist/src/core/repo-structure/role-prefix-collector.d.ts.map +1 -0
  36. package/dist/src/core/repo-structure/role-prefix-collector.js +106 -0
  37. package/dist/src/core/repo-structure/role-prefix-collector.js.map +1 -0
  38. package/dist/src/hooks/session-start.js +7 -0
  39. package/dist/src/hooks/session-start.js.map +1 -1
  40. package/dist/src/sync/story-router.d.ts +36 -0
  41. package/dist/src/sync/story-router.d.ts.map +1 -0
  42. package/dist/src/sync/story-router.js +56 -0
  43. package/dist/src/sync/story-router.js.map +1 -0
  44. package/dist/src/utils/state-cleanup.d.ts +19 -0
  45. package/dist/src/utils/state-cleanup.d.ts.map +1 -0
  46. package/dist/src/utils/state-cleanup.js +61 -0
  47. package/dist/src/utils/state-cleanup.js.map +1 -0
  48. package/package.json +1 -1
  49. 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":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAoB7E,MAAM,YAAY,GAAG;IACnB,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,UAAU,EAAE;IACxD,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,SAAS,EAAE;IACrD,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,QAAQ,EAAE;IACtD,EAAE,IAAI,EAAE,iCAAiC,EAAE,KAAK,EAAE,OAAO,EAAE;IAC3D,EAAE,IAAI,EAAE,+BAA+B,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC1D,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;CAClC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAAkB;IAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC,CAAC;IAEpG,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAE5C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC;YACxB,OAAO,EAAE,8BAA8B;YACvC,OAAO,EAAE,YAAY;SACtB,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QAE3D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;YACzB,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,aAAa;YACtB,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE;gBACxB,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;gBACrD,IAAI,CAAC,UAAU,CAAC,KAAK;oBAAE,OAAO,UAAU,CAAC,KAAM,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;SACF,CAAC,CAAC;QAEH,sBAAsB;QACtB,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACjE,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAE7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,IAAI,MAAM,UAAU,YAAY,CAAC,CAAC,CAAC;QAExE,MAAM,CAAC,IAAI,CAAC;YACV,GAAG,IAAI;YACP,IAAI;YACJ,WAAW,EAAE,UAAU;SACxB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA0C;IAE1C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC;IAE1D,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC;QAC/B,OAAO,EAAE;YACP,iDAAiD;YACjD,KAAK,CAAC,IAAI,CAAC,2EAA2E,CAAC;YACvF,KAAK,CAAC,IAAI,CAAC,kEAAkE,CAAC;YAC9E,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,kEAAkE,CAAC;YAC9E,KAAK,CAAC,IAAI,CAAC,8DAA8D,CAAC;YAC1E,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,oEAAoE,CAAC;YAChF,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC;YACvD,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,yEAAyE,CAAC;YACrF,KAAK,CAAC,IAAI,CAAC,8EAA8E,CAAC;SAC3F,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IAEH,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC,CAAC;QAC9E,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC;QAC5B,OAAO,EAAE,qDAAqD;QAC9D,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,EAAE;SACZ,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,QAAQ,kCAAkC,CAAC,CAAC,CAAC;IAEtF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -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":"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"}
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.253",
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 FULL user prompt so skill can extract all context
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
- # Escape the full prompt for JSON embedding
1393
- # Use printf to handle special chars, then escape for nested JSON
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=$(printf '%s' "$PROMPT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ')
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=$(printf '%s' "$PROMPT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ')
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=$(printf '%s' "$PROMPT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ')
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