safeword 0.48.0 → 0.51.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.
Files changed (68) hide show
  1. package/dist/{check-OFMUQD3Q.js → check-LDCKFJ2Y.js} +7 -6
  2. package/dist/{check-OFMUQD3Q.js.map → check-LDCKFJ2Y.js.map} +1 -1
  3. package/dist/{chunk-54KAYQTY.js → chunk-2WUL76K5.js} +2 -2
  4. package/dist/{chunk-42IGUF5V.js → chunk-AY7IBNF3.js} +5 -5
  5. package/dist/{chunk-UMPMYZ4F.js → chunk-GX7OYXXG.js} +3 -3
  6. package/dist/{chunk-2HB6H4G5.js → chunk-JLFYAVLP.js} +2 -2
  7. package/dist/{chunk-4J3GYDJF.js → chunk-LBAC7N4Y.js} +118 -120
  8. package/dist/chunk-LBAC7N4Y.js.map +1 -0
  9. package/dist/{chunk-ZNIJO52Z.js → chunk-MPEK5NNA.js} +2 -2
  10. package/dist/{chunk-445LAX4Y.js → chunk-MPYFFJBF.js} +24 -1
  11. package/dist/chunk-MPYFFJBF.js.map +1 -0
  12. package/dist/chunk-WE7ZQLCT.js +66 -0
  13. package/dist/chunk-WE7ZQLCT.js.map +1 -0
  14. package/dist/cli.js +16 -10
  15. package/dist/cli.js.map +1 -1
  16. package/dist/{codify-HIWCNPCY.js → codify-YUGZVCR4.js} +3 -3
  17. package/dist/{diff-SACXR7ES.js → diff-WQNR4ODP.js} +5 -4
  18. package/dist/{diff-SACXR7ES.js.map → diff-WQNR4ODP.js.map} +1 -1
  19. package/dist/index.d.ts +6 -0
  20. package/dist/{reset-AJ4B74Y2.js → reset-HY5GM2XR.js} +8 -6
  21. package/dist/{reset-AJ4B74Y2.js.map → reset-HY5GM2XR.js.map} +1 -1
  22. package/dist/{setup-IV3O3CKO.js → setup-I4UEA7LT.js} +13 -11
  23. package/dist/setup-I4UEA7LT.js.map +1 -0
  24. package/dist/{sync-config-ELXIXIY3.js → sync-config-5FCJLGMW.js} +3 -3
  25. package/dist/{sync-learnings-D2JYMRKZ.js → sync-learnings-C7GXSMKZ.js} +3 -3
  26. package/dist/{sync-tickets-YDFPSIVS.js → sync-tickets-SDX4XPVS.js} +4 -4
  27. package/dist/test-plan-4D3WNSQP.js +211 -0
  28. package/dist/test-plan-4D3WNSQP.js.map +1 -0
  29. package/dist/{ticket-new-BATYGHWL.js → ticket-new-CVBD3MVR.js} +3 -3
  30. package/dist/ticket-new-CVBD3MVR.js.map +1 -0
  31. package/dist/{upgrade-FYGFT3CJ.js → upgrade-HPGUYAGC.js} +11 -9
  32. package/dist/{upgrade-FYGFT3CJ.js.map → upgrade-HPGUYAGC.js.map} +1 -1
  33. package/package.json +4 -4
  34. package/templates/SAFEWORD.md +1 -1
  35. package/templates/commands/audit.md +27 -11
  36. package/templates/commands/self-review.md +12 -2
  37. package/templates/commands/verify.md +38 -18
  38. package/templates/doc-templates/task-spec-template.md +2 -2
  39. package/templates/guides/architecture-guide.md +1 -1
  40. package/templates/guides/data-architecture-guide.md +1 -0
  41. package/templates/guides/design-doc-guide.md +1 -0
  42. package/templates/guides/planning-guide.md +7 -5
  43. package/templates/hooks/lib/active-ticket.ts +3 -3
  44. package/templates/hooks/lib/test-runner.ts +90 -44
  45. package/templates/hooks/record-skill-invocation.ts +6 -3
  46. package/templates/hooks/stop-quality.ts +1 -1
  47. package/templates/hooks/write-review-stamp.ts +2 -1
  48. package/templates/skills/audit/SKILL.md +27 -11
  49. package/templates/skills/bdd/DISCOVERY.md +1 -1
  50. package/templates/skills/bdd/SCENARIOS.md +1 -1
  51. package/templates/skills/bdd/SKILL.md +2 -2
  52. package/templates/skills/debug/SKILL.md +1 -0
  53. package/templates/skills/self-review/SKILL.md +12 -3
  54. package/templates/skills/ticket-system/SKILL.md +5 -3
  55. package/templates/skills/verify/SKILL.md +38 -18
  56. package/dist/chunk-445LAX4Y.js.map +0 -1
  57. package/dist/chunk-4J3GYDJF.js.map +0 -1
  58. package/dist/setup-IV3O3CKO.js.map +0 -1
  59. package/dist/ticket-new-BATYGHWL.js.map +0 -1
  60. /package/dist/{chunk-54KAYQTY.js.map → chunk-2WUL76K5.js.map} +0 -0
  61. /package/dist/{chunk-42IGUF5V.js.map → chunk-AY7IBNF3.js.map} +0 -0
  62. /package/dist/{chunk-UMPMYZ4F.js.map → chunk-GX7OYXXG.js.map} +0 -0
  63. /package/dist/{chunk-2HB6H4G5.js.map → chunk-JLFYAVLP.js.map} +0 -0
  64. /package/dist/{chunk-ZNIJO52Z.js.map → chunk-MPEK5NNA.js.map} +0 -0
  65. /package/dist/{codify-HIWCNPCY.js.map → codify-YUGZVCR4.js.map} +0 -0
  66. /package/dist/{sync-config-ELXIXIY3.js.map → sync-config-5FCJLGMW.js.map} +0 -0
  67. /package/dist/{sync-learnings-D2JYMRKZ.js.map → sync-learnings-C7GXSMKZ.js.map} +0 -0
  68. /package/dist/{sync-tickets-YDFPSIVS.js.map → sync-tickets-SDX4XPVS.js.map} +0 -0
@@ -4,15 +4,16 @@ import {
4
4
  import {
5
5
  checkHealth,
6
6
  reportHealthSummary
7
- } from "./chunk-42IGUF5V.js";
7
+ } from "./chunk-AY7IBNF3.js";
8
8
  import {
9
9
  syncTickets
10
- } from "./chunk-2HB6H4G5.js";
10
+ } from "./chunk-JLFYAVLP.js";
11
11
  import "./chunk-NHXVS5FL.js";
12
12
  import "./chunk-IGULTNHR.js";
13
13
  import "./chunk-QLXFPFIC.js";
14
- import "./chunk-4J3GYDJF.js";
15
- import "./chunk-54KAYQTY.js";
14
+ import "./chunk-LBAC7N4Y.js";
15
+ import "./chunk-2WUL76K5.js";
16
+ import "./chunk-WE7ZQLCT.js";
16
17
  import "./chunk-LODQOJEK.js";
17
18
  import "./chunk-HSC7TELY.js";
18
19
  import {
@@ -21,7 +22,7 @@ import {
21
22
  keyValue,
22
23
  success,
23
24
  warn
24
- } from "./chunk-445LAX4Y.js";
25
+ } from "./chunk-MPYFFJBF.js";
25
26
 
26
27
  // src/commands/check.ts
27
28
  import process from "process";
@@ -109,4 +110,4 @@ async function check(options) {
109
110
  export {
110
111
  check
111
112
  };
112
- //# sourceMappingURL=check-OFMUQD3Q.js.map
113
+ //# sourceMappingURL=check-LDCKFJ2Y.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n *\n * The config-health core lives in ../health.ts (shared with the setup/upgrade\n * self-verify, ticket 3293WH). This command adds the standalone-only\n * surfaces: npm update-check, version display, and ticket-index refresh.\n */\n\nimport process from 'node:process';\n\nimport { checkHealth, type HealthStatus, reportHealthSummary } from '../health.js';\nimport { syncTickets } from '../ticket-sync/index.js';\nimport { header, info, keyValue, success, warn } from '../utils/output.js';\nimport { isNewerVersion } from '../utils/version.js';\n\ninterface CheckOptions {\n offline?: boolean;\n}\n\n/**\n * Check for latest version from npm (with timeout)\n * @param timeout\n */\nasync function checkLatestVersion(timeout = 3000): Promise<string | undefined> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, timeout);\n\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as { version?: string };\n return data.version ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Check for CLI updates and report status\n * @param health\n */\nasync function reportUpdateStatus(health: HealthStatus): Promise<void> {\n info('\\nChecking for updates...');\n const latestVersion = await checkLatestVersion();\n\n if (!latestVersion) {\n warn(\"Couldn't check for updates (offline?)\");\n return;\n }\n\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `bunx safeword@latest upgrade` to upgrade');\n } else {\n success('CLI is up to date');\n }\n}\n\n/**\n * Compare project version vs CLI version and report\n * @param health\n */\nfunction reportVersionMismatch(health: HealthStatus): void {\n if (!health.projectVersion) return;\n\n if (isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n info('Consider upgrading the CLI');\n } else if (isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n}\n\n/**\n * Regenerate the ticket discovery index, swallowing any error — index\n * freshness must never block or fail a health check. Reports only when it\n * actually rewrote a file.\n * @param cwd\n */\nfunction regenerateTicketIndex(cwd: string): void {\n try {\n const result = syncTickets(cwd);\n if (result.wrote) {\n info('Regenerated ticket index (INDEX.md / INDEX-completed.md)');\n }\n } catch (error: unknown) {\n // Best-effort: index freshness must never fail the health check. Surface\n // under DEBUG, then return — the deliberate swallow point.\n if (process.env.DEBUG) {\n console.error('[check] ticket index regen failed:', error);\n }\n return;\n }\n}\n\n/**\n *\n * @param options\n */\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = await checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Keep the ticket discovery index fresh at this checkpoint (best-effort —\n // never fail the health check on index regen). Ticket 1GGD28.\n regenerateTicketIndex(cwd);\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (options.offline) {\n info('\\nSkipped update check (offline mode)');\n } else {\n await reportUpdateStatus(health);\n }\n\n reportVersionMismatch(health);\n const hasIssues = reportHealthSummary(health);\n\n if (hasIssues) {\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,OAAO,aAAa;AAepB,eAAe,mBAAmB,UAAU,KAAmC;AAC7E,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM;AACjC,iBAAW,MAAM;AAAA,IACnB,GAAG,OAAO;AAEV,UAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,MACzE,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,SAAS;AAEtB,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAe,mBAAmB,QAAqC;AACrE,OAAK,2BAA2B;AAChC,QAAM,gBAAgB,MAAM,mBAAmB;AAE/C,MAAI,CAAC,eAAe;AAClB,SAAK,uCAAuC;AAC5C;AAAA,EACF;AAEA,SAAO,gBAAgB;AACvB,SAAO,kBAAkB,eAAe,OAAO,YAAY,aAAa;AAExE,MAAI,OAAO,iBAAiB;AAC1B,SAAK,sBAAsB,aAAa,EAAE;AAC1C,SAAK,+CAA+C;AAAA,EACtD,OAAO;AACL,YAAQ,mBAAmB;AAAA,EAC7B;AACF;AAMA,SAAS,sBAAsB,QAA4B;AACzD,MAAI,CAAC,OAAO,eAAgB;AAE5B,MAAI,eAAe,OAAO,YAAY,OAAO,cAAc,GAAG;AAC5D,SAAK,oBAAoB,OAAO,cAAc,yBAAyB,OAAO,UAAU,GAAG;AAC3F,SAAK,4BAA4B;AAAA,EACnC,WAAW,eAAe,OAAO,gBAAgB,OAAO,UAAU,GAAG;AACnE,SAAK;AAAA,qCAAwC;AAC7C;AAAA,MACE,4CAA4C,OAAO,cAAc,QAAQ,OAAO,UAAU;AAAA,IAC5F;AAAA,EACF;AACF;AAQA,SAAS,sBAAsB,KAAmB;AAChD,MAAI;AACF,UAAM,SAAS,YAAY,GAAG;AAC9B,QAAI,OAAO,OAAO;AAChB,WAAK,0DAA0D;AAAA,IACjE;AAAA,EACF,SAAS,OAAgB;AAGvB,QAAI,QAAQ,IAAI,OAAO;AACrB,cAAQ,MAAM,sCAAsC,KAAK;AAAA,IAC3D;AACA;AAAA,EACF;AACF;AAMA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AAExB,SAAO,uBAAuB;AAE9B,QAAM,SAAS,MAAM,YAAY,GAAG;AAGpC,MAAI,CAAC,OAAO,YAAY;AACtB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAIA,wBAAsB,GAAG;AAGzB,WAAS,gBAAgB,IAAI,OAAO,UAAU,EAAE;AAChD,WAAS,kBAAkB,OAAO,iBAAiB,IAAI,OAAO,cAAc,KAAK,SAAS;AAG1F,MAAI,QAAQ,SAAS;AACnB,SAAK,uCAAuC;AAAA,EAC9C,OAAO;AACL,UAAM,mBAAmB,MAAM;AAAA,EACjC;AAEA,wBAAsB,MAAM;AAC5B,QAAM,YAAY,oBAAoB,MAAM;AAE5C,MAAI,WAAW;AACb,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n *\n * The config-health core lives in ../health.ts (shared with the setup/upgrade\n * self-verify, ticket 3293WH). This command adds the standalone-only\n * surfaces: npm update-check, version display, and ticket-index refresh.\n */\n\nimport process from 'node:process';\n\nimport { checkHealth, type HealthStatus, reportHealthSummary } from '../health.js';\nimport { syncTickets } from '../ticket-sync/index.js';\nimport { header, info, keyValue, success, warn } from '../utils/output.js';\nimport { isNewerVersion } from '../utils/version.js';\n\ninterface CheckOptions {\n offline?: boolean;\n}\n\n/**\n * Check for latest version from npm (with timeout)\n * @param timeout\n */\nasync function checkLatestVersion(timeout = 3000): Promise<string | undefined> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, timeout);\n\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as { version?: string };\n return data.version ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Check for CLI updates and report status\n * @param health\n */\nasync function reportUpdateStatus(health: HealthStatus): Promise<void> {\n info('\\nChecking for updates...');\n const latestVersion = await checkLatestVersion();\n\n if (!latestVersion) {\n warn(\"Couldn't check for updates (offline?)\");\n return;\n }\n\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `bunx safeword@latest upgrade` to upgrade');\n } else {\n success('CLI is up to date');\n }\n}\n\n/**\n * Compare project version vs CLI version and report\n * @param health\n */\nfunction reportVersionMismatch(health: HealthStatus): void {\n if (!health.projectVersion) return;\n\n if (isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n info('Consider upgrading the CLI');\n } else if (isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n}\n\n/**\n * Regenerate the ticket discovery index, swallowing any error — index\n * freshness must never block or fail a health check. Reports only when it\n * actually rewrote a file.\n * @param cwd\n */\nfunction regenerateTicketIndex(cwd: string): void {\n try {\n const result = syncTickets(cwd);\n if (result.wrote) {\n info('Regenerated ticket index (INDEX.md / INDEX-completed.md)');\n }\n } catch (error: unknown) {\n // Best-effort: index freshness must never fail the health check. Surface\n // under DEBUG, then return — the deliberate swallow point.\n if (process.env.DEBUG) {\n console.error('[check] ticket index regen failed:', error);\n }\n return;\n }\n}\n\n/**\n *\n * @param options\n */\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = await checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Keep the ticket discovery index fresh at this checkpoint (best-effort —\n // never fail the health check on index regen). Ticket 1GGD28.\n regenerateTicketIndex(cwd);\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (options.offline) {\n info('\\nSkipped update check (offline mode)');\n } else {\n await reportUpdateStatus(health);\n }\n\n reportVersionMismatch(health);\n const hasIssues = reportHealthSummary(health);\n\n if (hasIssues) {\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,OAAO,aAAa;AAepB,eAAe,mBAAmB,UAAU,KAAmC;AAC7E,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM;AACjC,iBAAW,MAAM;AAAA,IACnB,GAAG,OAAO;AAEV,UAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,MACzE,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,SAAS;AAEtB,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAe,mBAAmB,QAAqC;AACrE,OAAK,2BAA2B;AAChC,QAAM,gBAAgB,MAAM,mBAAmB;AAE/C,MAAI,CAAC,eAAe;AAClB,SAAK,uCAAuC;AAC5C;AAAA,EACF;AAEA,SAAO,gBAAgB;AACvB,SAAO,kBAAkB,eAAe,OAAO,YAAY,aAAa;AAExE,MAAI,OAAO,iBAAiB;AAC1B,SAAK,sBAAsB,aAAa,EAAE;AAC1C,SAAK,+CAA+C;AAAA,EACtD,OAAO;AACL,YAAQ,mBAAmB;AAAA,EAC7B;AACF;AAMA,SAAS,sBAAsB,QAA4B;AACzD,MAAI,CAAC,OAAO,eAAgB;AAE5B,MAAI,eAAe,OAAO,YAAY,OAAO,cAAc,GAAG;AAC5D,SAAK,oBAAoB,OAAO,cAAc,yBAAyB,OAAO,UAAU,GAAG;AAC3F,SAAK,4BAA4B;AAAA,EACnC,WAAW,eAAe,OAAO,gBAAgB,OAAO,UAAU,GAAG;AACnE,SAAK;AAAA,qCAAwC;AAC7C;AAAA,MACE,4CAA4C,OAAO,cAAc,QAAQ,OAAO,UAAU;AAAA,IAC5F;AAAA,EACF;AACF;AAQA,SAAS,sBAAsB,KAAmB;AAChD,MAAI;AACF,UAAM,SAAS,YAAY,GAAG;AAC9B,QAAI,OAAO,OAAO;AAChB,WAAK,0DAA0D;AAAA,IACjE;AAAA,EACF,SAAS,OAAgB;AAGvB,QAAI,QAAQ,IAAI,OAAO;AACrB,cAAQ,MAAM,sCAAsC,KAAK;AAAA,IAC3D;AACA;AAAA,EACF;AACF;AAMA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AAExB,SAAO,uBAAuB;AAE9B,QAAM,SAAS,MAAM,YAAY,GAAG;AAGpC,MAAI,CAAC,OAAO,YAAY;AACtB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAIA,wBAAsB,GAAG;AAGzB,WAAS,gBAAgB,IAAI,OAAO,UAAU,EAAE;AAChD,WAAS,kBAAkB,OAAO,iBAAiB,IAAI,OAAO,cAAc,KAAK,SAAS;AAG1F,MAAI,QAAQ,SAAS;AACnB,SAAK,uCAAuC;AAAA,EAC9C,OAAO;AACL,UAAM,mBAAmB,MAAM;AAAA,EACjC;AAEA,wBAAsB,MAAM;AAC5B,QAAM,YAAY,oBAAoB,MAAM;AAE5C,MAAI,WAAW;AACb,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  isDirectory,
3
3
  readFileSafe
4
- } from "./chunk-445LAX4Y.js";
4
+ } from "./chunk-MPYFFJBF.js";
5
5
 
6
6
  // src/utils/configured-paths.ts
7
7
  import nodePath from "path";
@@ -117,4 +117,4 @@ export {
117
117
  defaultConfiguredPath,
118
118
  resolveConfiguredPath
119
119
  };
120
- //# sourceMappingURL=chunk-54KAYQTY.js.map
120
+ //# sourceMappingURL=chunk-2WUL76K5.js.map
@@ -2,7 +2,7 @@ import {
2
2
  findDanglingDependencies,
3
3
  findTicketsInCycles,
4
4
  readTickets
5
- } from "./chunk-2HB6H4G5.js";
5
+ } from "./chunk-JLFYAVLP.js";
6
6
  import {
7
7
  formatTicketReference
8
8
  } from "./chunk-NHXVS5FL.js";
@@ -22,14 +22,14 @@ import {
22
22
  createProjectContext,
23
23
  getMissingPacks,
24
24
  reconcile
25
- } from "./chunk-4J3GYDJF.js";
25
+ } from "./chunk-LBAC7N4Y.js";
26
26
  import {
27
27
  defaultConfiguredPath,
28
28
  readConfiguredDocumentationSources,
29
29
  readConfiguredPath,
30
30
  resolveConfiguredPath,
31
31
  resolveTicketsDirectory
32
- } from "./chunk-54KAYQTY.js";
32
+ } from "./chunk-2WUL76K5.js";
33
33
  import {
34
34
  VERSION
35
35
  } from "./chunk-HSC7TELY.js";
@@ -42,7 +42,7 @@ import {
42
42
  readFileSafe,
43
43
  success,
44
44
  warn
45
- } from "./chunk-445LAX4Y.js";
45
+ } from "./chunk-MPYFFJBF.js";
46
46
 
47
47
  // src/health.ts
48
48
  import { readdirSync as readdirSync2 } from "fs";
@@ -698,4 +698,4 @@ export {
698
698
  checkHealth,
699
699
  reportHealthSummary
700
700
  };
701
- //# sourceMappingURL=chunk-42IGUF5V.js.map
701
+ //# sourceMappingURL=chunk-AY7IBNF3.js.map
@@ -3,7 +3,7 @@ import {
3
3
  addInstalledPack,
4
4
  isGitRepo,
5
5
  isPackInstalled
6
- } from "./chunk-4J3GYDJF.js";
6
+ } from "./chunk-LBAC7N4Y.js";
7
7
  import {
8
8
  SAFEWORD_PEER_DEPENDENCIES
9
9
  } from "./chunk-HSC7TELY.js";
@@ -13,7 +13,7 @@ import {
13
13
  listItem,
14
14
  readJson,
15
15
  warn
16
- } from "./chunk-445LAX4Y.js";
16
+ } from "./chunk-MPYFFJBF.js";
17
17
 
18
18
  // src/packs/install.ts
19
19
  function installPack(packId, cwd) {
@@ -390,4 +390,4 @@ export {
390
390
  getEslintPeerMismatchWarning,
391
391
  maybeAutoPatchOrNudge
392
392
  };
393
- //# sourceMappingURL=chunk-UMPMYZ4F.js.map
393
+ //# sourceMappingURL=chunk-GX7OYXXG.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-NHXVS5FL.js";
4
4
  import {
5
5
  resolveTicketsDirectory
6
- } from "./chunk-54KAYQTY.js";
6
+ } from "./chunk-2WUL76K5.js";
7
7
 
8
8
  // src/ticket-sync/index.ts
9
9
  import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
@@ -244,4 +244,4 @@ export {
244
244
  readTickets,
245
245
  syncTickets
246
246
  };
247
- //# sourceMappingURL=chunk-2HB6H4G5.js.map
247
+ //# sourceMappingURL=chunk-JLFYAVLP.js.map
@@ -2,7 +2,10 @@ import {
2
2
  NAMESPACE_ROOT_LEGACY,
3
3
  readConfiguredPath,
4
4
  resolveNamespaceRoot
5
- } from "./chunk-54KAYQTY.js";
5
+ } from "./chunk-2WUL76K5.js";
6
+ import {
7
+ MCP_SERVERS
8
+ } from "./chunk-WE7ZQLCT.js";
6
9
  import {
7
10
  detect
8
11
  } from "./chunk-LODQOJEK.js";
@@ -15,19 +18,15 @@ import {
15
18
  existsInTree,
16
19
  findInTree,
17
20
  getTemplatesDirectory,
18
- info,
19
- listItem,
20
21
  makeScriptsExecutable,
21
22
  readFile,
22
23
  readFileSafe,
23
24
  readJson,
24
25
  remove,
25
26
  removeIfEmpty,
26
- success,
27
- warn,
28
27
  writeFile,
29
28
  writeJson
30
- } from "./chunk-445LAX4Y.js";
29
+ } from "./chunk-MPYFFJBF.js";
31
30
 
32
31
  // src/packs/config.ts
33
32
  import nodePath from "path";
@@ -496,8 +495,8 @@ function getConditionalPackages(conditionalPackages, projectType) {
496
495
  }
497
496
  return packages;
498
497
  }
499
- function shouldSkipForNonGit(path2, isGitRepo2) {
500
- return path2.startsWith(HUSKY_DIR) && !isGitRepo2;
498
+ function shouldSkipForNonGit(path, isGitRepo2) {
499
+ return path.startsWith(HUSKY_DIR) && !isGitRepo2;
501
500
  }
502
501
  function planMissingDirectories(directories, cwd, isGitRepo2) {
503
502
  const actions = [];
@@ -607,19 +606,19 @@ function withResolvedNamespaceRoot(schema, ctx) {
607
606
  const root = ctx.namespaceRoot ?? resolveNamespaceRoot(ctx.cwd);
608
607
  const label = nodePath7.relative(ctx.cwd, root) || ".";
609
608
  if (label === NAMESPACE_ROOT_LEGACY) return schema;
610
- const translate = (path2) => {
611
- if (path2 !== NAMESPACE_ROOT_LEGACY && !path2.startsWith(`${NAMESPACE_ROOT_LEGACY}/`)) {
612
- return path2;
609
+ const translate = (path) => {
610
+ if (path !== NAMESPACE_ROOT_LEGACY && !path.startsWith(`${NAMESPACE_ROOT_LEGACY}/`)) {
611
+ return path;
613
612
  }
614
- const subpath = path2.slice(NAMESPACE_ROOT_LEGACY.length).replace(/^\//, "");
613
+ const subpath = path.slice(NAMESPACE_ROOT_LEGACY.length).replace(/^\//, "");
615
614
  return label === "." ? subpath || "." : nodePath7.join(label, subpath);
616
615
  };
617
616
  return {
618
617
  ...schema,
619
- preservedDirs: schema.preservedDirs.map((path2) => translate(path2)),
618
+ preservedDirs: schema.preservedDirs.map((path) => translate(path)),
620
619
  managedFiles: Object.fromEntries(
621
- Object.entries(schema.managedFiles).map(([path2, definition]) => [
622
- translate(path2),
620
+ Object.entries(schema.managedFiles).map(([path, definition]) => [
621
+ translate(path),
623
622
  definition
624
623
  ])
625
624
  )
@@ -849,13 +848,13 @@ function executePlan(plan, ctx) {
849
848
  return result;
850
849
  }
851
850
  function executeChmod(cwd, paths) {
852
- for (const path2 of paths) {
853
- const fullPath = nodePath7.join(cwd, path2);
851
+ for (const path of paths) {
852
+ const fullPath = nodePath7.join(cwd, path);
854
853
  if (exists(fullPath)) makeScriptsExecutable(fullPath);
855
854
  }
856
855
  }
857
- function executeRmdir(cwd, path2, result) {
858
- if (removeIfEmpty(nodePath7.join(cwd, path2))) result.removed.push(path2);
856
+ function executeRmdir(cwd, path, result) {
857
+ if (removeIfEmpty(nodePath7.join(cwd, path))) result.removed.push(path);
859
858
  }
860
859
  function executeAction(action, ctx, result) {
861
860
  switch (action.type) {
@@ -899,11 +898,11 @@ function executeAction(action, ctx, result) {
899
898
  }
900
899
  }
901
900
  }
902
- function executeWrite(cwd, path2, content, result) {
903
- const fullPath = nodePath7.join(cwd, path2);
901
+ function executeWrite(cwd, path, content, result) {
902
+ const fullPath = nodePath7.join(cwd, path);
904
903
  const existed = exists(fullPath);
905
904
  writeFile(fullPath, content);
906
- (existed ? result.updated : result.created).push(path2);
905
+ (existed ? result.updated : result.created).push(path);
907
906
  }
908
907
  function resolveFileContent(definition, ctx) {
909
908
  if (definition.template) {
@@ -945,8 +944,8 @@ function stripVersionSpecifier(pkg) {
945
944
  const atIndex = pkg.startsWith("@") ? pkg.indexOf("@", 1) : pkg.indexOf("@");
946
945
  return atIndex === -1 ? pkg : pkg.slice(0, atIndex);
947
946
  }
948
- function executeJsonMerge(cwd, path2, definition, ctx) {
949
- const fullPath = nodePath7.join(cwd, path2);
947
+ function executeJsonMerge(cwd, path, definition, ctx) {
948
+ const fullPath = nodePath7.join(cwd, path);
950
949
  const rawExisting = readJson(fullPath);
951
950
  if (!rawExisting && definition.skipIfMissing) return;
952
951
  const existing = rawExisting ?? {};
@@ -954,8 +953,8 @@ function executeJsonMerge(cwd, path2, definition, ctx) {
954
953
  if (JSON.stringify(existing) === JSON.stringify(merged)) return;
955
954
  writeJson(fullPath, merged);
956
955
  }
957
- function executeJsonUnmerge(cwd, path2, definition, ctx) {
958
- const fullPath = nodePath7.join(cwd, path2);
956
+ function executeJsonUnmerge(cwd, path, definition, ctx) {
957
+ const fullPath = nodePath7.join(cwd, path);
959
958
  if (!exists(fullPath)) return;
960
959
  const existing = readJson(fullPath);
961
960
  if (!existing) return;
@@ -969,8 +968,8 @@ function executeJsonUnmerge(cwd, path2, definition, ctx) {
969
968
  }
970
969
  writeJson(fullPath, unmerged);
971
970
  }
972
- function executeTextPatch(cwd, path2, definition) {
973
- const fullPath = nodePath7.join(cwd, path2);
971
+ function executeTextPatch(cwd, path, definition) {
972
+ const fullPath = nodePath7.join(cwd, path);
974
973
  let content = readFileSafe(fullPath) ?? "";
975
974
  if (content.includes(definition.marker)) {
976
975
  if (content.includes("\n\n---#")) {
@@ -982,8 +981,8 @@ function executeTextPatch(cwd, path2, definition) {
982
981
  content = definition.operation === "prepend" ? definition.content + content : content + definition.content;
983
982
  writeFile(fullPath, content);
984
983
  }
985
- function executeTextUnpatch(cwd, path2, definition) {
986
- const fullPath = nodePath7.join(cwd, path2);
984
+ function executeTextUnpatch(cwd, path, definition) {
985
+ const fullPath = nodePath7.join(cwd, path);
987
986
  const content = readFileSafe(fullPath);
988
987
  if (!content) return;
989
988
  let unpatched = removeExactTextPatchContent(content, definition);
@@ -1022,58 +1021,6 @@ function shouldRemoveTextPatchTarget(content, definition) {
1022
1021
  return definition.removeFileIfContentEquals?.some((candidate) => trimmed === candidate.trim()) ?? false;
1023
1022
  }
1024
1023
 
1025
- // src/utils/install.ts
1026
- import { execFileSync } from "child_process";
1027
- import { existsSync as existsSync3 } from "fs";
1028
- import path from "path";
1029
- var DEV_FLAG = "-D";
1030
- var PM_COMMANDS = {
1031
- npm: { install: "install", uninstall: "uninstall" },
1032
- yarn: { install: "add", uninstall: "remove" },
1033
- pnpm: { install: "add", uninstall: "remove" },
1034
- bun: { install: "add", uninstall: "remove" }
1035
- };
1036
- function detectPackageManager(cwd) {
1037
- if (existsSync3(path.join(cwd, "bun.lockb")) || existsSync3(path.join(cwd, "bun.lock")))
1038
- return "bun";
1039
- if (existsSync3(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1040
- if (existsSync3(path.join(cwd, "yarn.lock"))) return "yarn";
1041
- if (existsSync3(path.join(cwd, "package-lock.json"))) return "npm";
1042
- if (process.versions.bun) return "bun";
1043
- return "npm";
1044
- }
1045
- function getUninstallCommand(pm, packages) {
1046
- const { uninstall } = PM_COMMANDS[pm];
1047
- return `${pm} ${uninstall} ${packages.join(" ")}`;
1048
- }
1049
- function installDependencies(cwd, packages, label = "packages") {
1050
- if (packages.length === 0) return;
1051
- if (process.env.SAFEWORD_SKIP_INSTALL) return;
1052
- const pm = detectPackageManager(cwd);
1053
- const { install } = PM_COMMANDS[pm];
1054
- const displayCommand = `${pm} ${install} ${DEV_FLAG} ${packages.join(" ")}`;
1055
- info(`
1056
- Installing ${label}...`);
1057
- info(`Running: ${displayCommand}`);
1058
- try {
1059
- execFileSync(pm, [install, DEV_FLAG, ...packages], { cwd, stdio: "pipe", timeout: 12e4 });
1060
- success(`Installed ${label}`);
1061
- } catch {
1062
- warn(`Failed to install ${label}. Run manually:`);
1063
- listItem(displayCommand);
1064
- }
1065
- }
1066
- var MCP_SERVERS = {
1067
- context7: {
1068
- command: "bunx",
1069
- args: ["@upstash/context7-mcp@latest"]
1070
- },
1071
- playwright: {
1072
- command: "bunx",
1073
- args: ["@playwright/mcp@latest"]
1074
- }
1075
- };
1076
-
1077
1024
  // src/packs/golang/files.ts
1078
1025
  import { readFileSync as readFileSync2 } from "fs";
1079
1026
  import nodePath8 from "path";
@@ -1281,11 +1228,11 @@ var golangManagedFiles = {
1281
1228
  };
1282
1229
 
1283
1230
  // src/packs/python/files.ts
1284
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1231
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1285
1232
  import nodePath9 from "path";
1286
1233
  function hasLegacyCustomerRuffExtend(cwd) {
1287
1234
  const ruffPath = nodePath9.join(cwd, "ruff.toml");
1288
- if (!existsSync4(ruffPath)) return false;
1235
+ if (!existsSync3(ruffPath)) return false;
1289
1236
  try {
1290
1237
  const content = readFileSync3(ruffPath, "utf8");
1291
1238
  return content.includes('extend = ".safeword/ruff.toml"');
@@ -1446,7 +1393,7 @@ var pythonManagedFiles = {
1446
1393
  };
1447
1394
 
1448
1395
  // src/packs/rust/files.ts
1449
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1396
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1450
1397
  import nodePath10 from "path";
1451
1398
  var SAFEWORD_CLIPPY_THRESHOLDS = {
1452
1399
  "cognitive-complexity-threshold": 10,
@@ -1471,7 +1418,7 @@ ${generateClippyThresholds()}
1471
1418
  `;
1472
1419
  }
1473
1420
  const configPath = nodePath10.join(cwd, existingConfig);
1474
- if (!existsSync5(configPath)) {
1421
+ if (!existsSync4(configPath)) {
1475
1422
  return `# Safeword clippy config - standalone (no project config to merge)
1476
1423
  # Used by hooks for LLM enforcement.
1477
1424
 
@@ -1546,7 +1493,7 @@ var rustManagedFiles = {
1546
1493
  };
1547
1494
 
1548
1495
  // src/packs/sql/dialect.ts
1549
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1496
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1550
1497
  import nodePath11 from "path";
1551
1498
  import process3 from "process";
1552
1499
  import YAML2 from "yaml";
@@ -1619,7 +1566,7 @@ function detectSqlDialect(cwd) {
1619
1566
  return detectFromProfiles(cwd) ?? detectFromPythonDeps(cwd) ?? detectFromSqlcConfig(cwd) ?? detectFromPrismaSchema(cwd) ?? detectFromDrizzleConfig(cwd) ?? detectFromDatabaseUrl(cwd);
1620
1567
  }
1621
1568
  function resolveProfileType(profilesPath, profileName) {
1622
- if (!existsSync6(profilesPath)) return void 0;
1569
+ if (!existsSync5(profilesPath)) return void 0;
1623
1570
  const content = readFileSync5(profilesPath, "utf8");
1624
1571
  const profiles = YAML2.parse(content, { schema: "failsafe" });
1625
1572
  const profile = profiles[profileName];
@@ -1662,9 +1609,9 @@ function detectFromPythonDeps(cwd) {
1662
1609
  const pyprojectPath = nodePath11.join(directory, "pyproject.toml");
1663
1610
  try {
1664
1611
  let content;
1665
- if (existsSync6(requirementsPath)) {
1612
+ if (existsSync5(requirementsPath)) {
1666
1613
  content = readFileSync5(requirementsPath, "utf8");
1667
- } else if (existsSync6(pyprojectPath)) {
1614
+ } else if (existsSync5(pyprojectPath)) {
1668
1615
  content = readFileSync5(pyprojectPath, "utf8");
1669
1616
  }
1670
1617
  if (!content) return void 0;
@@ -1694,7 +1641,7 @@ function detectFromSqlcConfig(cwd) {
1694
1641
  }
1695
1642
  function detectFromPrismaSchema(cwd) {
1696
1643
  const schemaPath = nodePath11.join(cwd, "prisma", "schema.prisma");
1697
- if (!existsSync6(schemaPath)) return void 0;
1644
+ if (!existsSync5(schemaPath)) return void 0;
1698
1645
  try {
1699
1646
  const content = readFileSync5(schemaPath, "utf8");
1700
1647
  const match = /provider\s*=\s*"(\w+)"/.exec(content);
@@ -1707,7 +1654,7 @@ function detectFromPrismaSchema(cwd) {
1707
1654
  function detectFromDrizzleConfig(cwd) {
1708
1655
  for (const filename of ["drizzle.config.ts", "drizzle.config.js", "drizzle.config.mjs"]) {
1709
1656
  const filePath = nodePath11.join(cwd, filename);
1710
- if (!existsSync6(filePath)) continue;
1657
+ if (!existsSync5(filePath)) continue;
1711
1658
  try {
1712
1659
  const content = readFileSync5(filePath, "utf8");
1713
1660
  const match = /dialect\s*:\s*["'](\w+)["']/.exec(content);
@@ -1720,7 +1667,7 @@ function detectFromDrizzleConfig(cwd) {
1720
1667
  }
1721
1668
  function detectFromDatabaseUrl(cwd) {
1722
1669
  const envPath = nodePath11.join(cwd, ".env");
1723
- if (!existsSync6(envPath)) return void 0;
1670
+ if (!existsSync5(envPath)) return void 0;
1724
1671
  try {
1725
1672
  const content = readFileSync5(envPath, "utf8");
1726
1673
  const match = /DATABASE_URL\s*=\s*["']?(\w+):/.exec(content);
@@ -2229,7 +2176,7 @@ var typescriptManagedFiles = {
2229
2176
  // Knip config for dead code detection (used by /audit)
2230
2177
  // Plugins auto-enable based on deps, we just configure ignore patterns
2231
2178
  "knip.json": {
2232
- generator: (ctx) => ctx.languages?.javascript ? JSON.stringify(getKnipConfig(ctx), void 0, 2) : void 0
2179
+ generator: (ctx) => ctx.projectType?.hasJsSource ? JSON.stringify(getKnipConfig(ctx), void 0, 2) : void 0
2233
2180
  },
2234
2181
  // Project-level Prettier config (created only if no existing formatter)
2235
2182
  ".prettierrc": {
@@ -2266,7 +2213,6 @@ var typescriptJsonMerges = {
2266
2213
  "scripts.lint:gherkin",
2267
2214
  "scripts.format",
2268
2215
  "scripts.format:check",
2269
- "scripts.knip",
2270
2216
  "scripts.test:bdd"
2271
2217
  ],
2272
2218
  skipIfMissing: true,
@@ -2275,14 +2221,16 @@ var typescriptJsonMerges = {
2275
2221
  existingLinter: ["scripts.lint:eslint"],
2276
2222
  // Projects with existing linter get separate ESLint script
2277
2223
  publishableLibrary: ["scripts.publint"],
2278
- shell: ["scripts.lint:sh"]
2224
+ shell: ["scripts.lint:sh"],
2225
+ hasJsSource: ["scripts.knip"]
2226
+ // knip script only added for repos with real JS source (BE7C7B)
2279
2227
  },
2280
2228
  merge: (existing, ctx) => {
2281
2229
  const scripts = { ...existing.scripts };
2282
2230
  const result = { ...existing };
2283
2231
  mergeLintScripts(scripts, ctx.projectType);
2284
2232
  mergeFormatScripts(scripts, ctx.projectType);
2285
- addScriptIfMissing(scripts, "knip", "knip");
2233
+ if (ctx.projectType.hasJsSource) addScriptIfMissing(scripts, "knip", "knip");
2286
2234
  addScriptIfMissing(scripts, "test:bdd", "cucumber-js");
2287
2235
  if (ctx.projectType.publishableLibrary) {
2288
2236
  addScriptIfMissing(scripts, "publint", "publint");
@@ -2364,13 +2312,10 @@ var typescriptJsonMerges = {
2364
2312
  var ESLINT_PACKAGE = "eslint@^9.22.0";
2365
2313
  var typescriptPackages = {
2366
2314
  base: [
2367
- // Core tools (always needed for JS/TS)
2315
+ // Core tools (always needed for JS/TS — eslint/prettier also lint the BDD lane's .ts step files)
2368
2316
  ESLINT_PACKAGE,
2369
2317
  // Safeword (bundles eslint-config-prettier + all ESLint plugins)
2370
2318
  "safeword",
2371
- // Architecture and dead code tools (used by /audit)
2372
- "dependency-cruiser",
2373
- "knip",
2374
2319
  // BDD acceptance lane (ticket 102b) — cucumber-js runs the scaffolded
2375
2320
  // .feature files; tsx transpiles the TypeScript step definitions, and
2376
2321
  // @types/node lets the scaffolded steps (node: imports) pass typechecks.
@@ -2391,7 +2336,11 @@ var typescriptPackages = {
2391
2336
  shellcheck: ["shellcheck"],
2392
2337
  // Renamed from shell to avoid conflict with prettier-plugin-sh
2393
2338
  // Legacy ESLint config compat (needed when extending .eslintrc.* configs)
2394
- legacyEslint: ["@eslint/eslintrc"]
2339
+ legacyEslint: ["@eslint/eslintrc"],
2340
+ // Architecture + dead-code tools (used by /audit) — these scan JS APPLICATION
2341
+ // code/deps, so only for repos with real JS source, not a non-JS repo carrying
2342
+ // just the TS BDD lane (BE7C7B).
2343
+ hasJsSource: ["dependency-cruiser", "knip"]
2395
2344
  }
2396
2345
  };
2397
2346
 
@@ -2404,9 +2353,9 @@ function computeSafewordPathPrefixes(schema) {
2404
2353
  ...Object.keys(schema.textPatches)
2405
2354
  ];
2406
2355
  const prefixes = /* @__PURE__ */ new Set();
2407
- for (const path2 of allPaths) {
2408
- const slashIndex = path2.indexOf("/");
2409
- prefixes.add(slashIndex === -1 ? path2 : path2.slice(0, slashIndex + 1));
2356
+ for (const path of allPaths) {
2357
+ const slashIndex = path.indexOf("/");
2358
+ prefixes.add(slashIndex === -1 ? path : path.slice(0, slashIndex + 1));
2410
2359
  }
2411
2360
  return [...prefixes].toSorted((a, b) => a.localeCompare(b));
2412
2361
  }
@@ -3333,7 +3282,7 @@ ${SAFEWORD_TRANSIENT_PATHS.join("\n")}
3333
3282
  };
3334
3283
 
3335
3284
  // src/utils/git.ts
3336
- import { execFileSync as execFileSync2 } from "child_process";
3285
+ import { execFileSync } from "child_process";
3337
3286
  import nodePath12 from "path";
3338
3287
  function isGitRepo(cwd) {
3339
3288
  return exists(nodePath12.join(cwd, ".git"));
@@ -3342,7 +3291,7 @@ function untrackIgnoredFiles(cwd, paths) {
3342
3291
  if (!isGitRepo(cwd)) return;
3343
3292
  for (const pattern of paths) {
3344
3293
  try {
3345
- execFileSync2("git", ["rm", "--cached", "--ignore-unmatch", "--", pattern], {
3294
+ execFileSync("git", ["rm", "--cached", "--ignore-unmatch", "--", pattern], {
3346
3295
  cwd,
3347
3296
  stdio: "pipe"
3348
3297
  });
@@ -3355,7 +3304,7 @@ function untrackIgnoredFiles(cwd, paths) {
3355
3304
  import nodePath14 from "path";
3356
3305
 
3357
3306
  // src/utils/project-detector.ts
3358
- import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "fs";
3307
+ import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "fs";
3359
3308
  import nodePath13 from "path";
3360
3309
  var {
3361
3310
  TAILWIND_PACKAGES,
@@ -3416,15 +3365,15 @@ function hasShellScripts(cwd, maxDepth = 4) {
3416
3365
  return scan(cwd, 0);
3417
3366
  }
3418
3367
  function findFirstExisting(cwd, candidates) {
3419
- return candidates.find((file) => existsSync7(nodePath13.join(cwd, file)));
3368
+ return candidates.find((file) => existsSync6(nodePath13.join(cwd, file)));
3420
3369
  }
3421
3370
  function findExistingEslintConfig(cwd) {
3422
3371
  return findFirstExisting(cwd, ESLINT_CONFIG_FILES);
3423
3372
  }
3424
3373
  function findExistingRuffConfig(cwd) {
3425
- if (existsSync7(nodePath13.join(cwd, "ruff.toml"))) return "ruff.toml";
3374
+ if (existsSync6(nodePath13.join(cwd, "ruff.toml"))) return "ruff.toml";
3426
3375
  const pyprojectPath = nodePath13.join(cwd, PYPROJECT_TOML);
3427
- if (!existsSync7(pyprojectPath)) return void 0;
3376
+ if (!existsSync6(pyprojectPath)) return void 0;
3428
3377
  try {
3429
3378
  const content = readFileSync6(pyprojectPath, "utf8");
3430
3379
  return content.includes("[tool.ruff]") ? "pyproject.toml" : void 0;
@@ -3433,10 +3382,10 @@ function findExistingRuffConfig(cwd) {
3433
3382
  }
3434
3383
  }
3435
3384
  function hasExistingMypyConfig(cwd) {
3436
- if (existsSync7(nodePath13.join(cwd, "mypy.ini"))) return true;
3437
- if (existsSync7(nodePath13.join(cwd, ".mypy.ini"))) return true;
3385
+ if (existsSync6(nodePath13.join(cwd, "mypy.ini"))) return true;
3386
+ if (existsSync6(nodePath13.join(cwd, ".mypy.ini"))) return true;
3438
3387
  const pyprojectPath = nodePath13.join(cwd, PYPROJECT_TOML);
3439
- if (!existsSync7(pyprojectPath)) return false;
3388
+ if (!existsSync6(pyprojectPath)) return false;
3440
3389
  try {
3441
3390
  const content = readFileSync6(pyprojectPath, "utf8");
3442
3391
  return content.includes("[tool.mypy]");
@@ -3445,9 +3394,9 @@ function hasExistingMypyConfig(cwd) {
3445
3394
  }
3446
3395
  }
3447
3396
  function hasExistingImportLinterConfig(cwd) {
3448
- if (existsSync7(nodePath13.join(cwd, ".importlinter"))) return true;
3397
+ if (existsSync6(nodePath13.join(cwd, ".importlinter"))) return true;
3449
3398
  const pyprojectPath = nodePath13.join(cwd, PYPROJECT_TOML);
3450
- if (!existsSync7(pyprojectPath)) return false;
3399
+ if (!existsSync6(pyprojectPath)) return false;
3451
3400
  try {
3452
3401
  const content = readFileSync6(pyprojectPath, "utf8");
3453
3402
  return content.includes("[tool.importlinter]");
@@ -3500,6 +3449,57 @@ function detectExistingTooling(cwd, scripts) {
3500
3449
  ...detectSystemsTooling(cwd)
3501
3450
  };
3502
3451
  }
3452
+ var JS_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
3453
+ ".ts",
3454
+ ".tsx",
3455
+ ".js",
3456
+ ".jsx",
3457
+ ".mjs",
3458
+ ".cjs",
3459
+ ".mts",
3460
+ ".cts"
3461
+ ]);
3462
+ var JS_SOURCE_DIR_EXCLUDE = /* @__PURE__ */ new Set([
3463
+ "node_modules",
3464
+ ".git",
3465
+ ".safeword",
3466
+ "dist",
3467
+ "build",
3468
+ "target",
3469
+ "coverage",
3470
+ "vendor"
3471
+ ]);
3472
+ var LANE_DIRS = /* @__PURE__ */ new Set(["steps", "features"]);
3473
+ var LANE_FILES = /* @__PURE__ */ new Set(["cucumber.mjs", "eslint.config.mjs", ".prettierrc"]);
3474
+ function isExcludedSourceDirectory(name, depth) {
3475
+ if (name.startsWith(".") || JS_SOURCE_DIR_EXCLUDE.has(name)) return true;
3476
+ return depth === 0 && LANE_DIRS.has(name);
3477
+ }
3478
+ function isJsSourceFile(name, depth) {
3479
+ if (depth === 0 && LANE_FILES.has(name)) return false;
3480
+ return JS_SOURCE_EXTENSIONS.has(nodePath13.extname(name));
3481
+ }
3482
+ function scanForJsSource(directory, depth, maxDepth) {
3483
+ let entries;
3484
+ try {
3485
+ entries = readdirSync4(directory, { withFileTypes: true });
3486
+ } catch {
3487
+ return false;
3488
+ }
3489
+ for (const entry of entries) {
3490
+ if (entry.isFile()) {
3491
+ if (isJsSourceFile(entry.name, depth)) return true;
3492
+ continue;
3493
+ }
3494
+ if (!entry.isDirectory() || depth >= maxDepth || isExcludedSourceDirectory(entry.name, depth))
3495
+ continue;
3496
+ if (scanForJsSource(nodePath13.join(directory, entry.name), depth + 1, maxDepth)) return true;
3497
+ }
3498
+ return false;
3499
+ }
3500
+ function hasJsSource(cwd, maxDepth = 6) {
3501
+ return scanForJsSource(cwd, 0, maxDepth);
3502
+ }
3503
3503
  function detectProjectType(packageJson, cwd) {
3504
3504
  const deps = packageJson.dependencies ?? {};
3505
3505
  const developmentDeps = packageJson.devDependencies ?? {};
@@ -3509,6 +3509,7 @@ function detectProjectType(packageJson, cwd) {
3509
3509
  ...detectFrameworks(deps, developmentDeps, allDeps),
3510
3510
  publishableLibrary: detectPublishable(packageJson),
3511
3511
  shell: cwd ? hasShellScripts(cwd) : false,
3512
+ hasJsSource: cwd ? hasJsSource(cwd) : false,
3512
3513
  ...detectExistingTooling(cwd, scripts)
3513
3514
  };
3514
3515
  }
@@ -3542,13 +3543,10 @@ export {
3542
3543
  getMissingPacks,
3543
3544
  getWorkspacePatterns,
3544
3545
  reconcile,
3545
- detectPackageManager,
3546
- getUninstallCommand,
3547
- installDependencies,
3548
3546
  SAFEWORD_TRANSIENT_PATHS,
3549
3547
  SAFEWORD_SCHEMA,
3550
3548
  isGitRepo,
3551
3549
  untrackIgnoredFiles,
3552
3550
  createProjectContext
3553
3551
  };
3554
- //# sourceMappingURL=chunk-4J3GYDJF.js.map
3552
+ //# sourceMappingURL=chunk-LBAC7N4Y.js.map