safeword 0.8.6 → 0.8.9

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 (39) hide show
  1. package/dist/{check-I2J6THGQ.js → check-QMAGWUOA.js} +24 -22
  2. package/dist/check-QMAGWUOA.js.map +1 -0
  3. package/dist/{chunk-DES5CSPH.js → chunk-4URRFBUS.js} +10 -10
  4. package/dist/chunk-4URRFBUS.js.map +1 -0
  5. package/dist/{chunk-DXT6TWW4.js → chunk-CLSGXTOL.js} +232 -435
  6. package/dist/chunk-CLSGXTOL.js.map +1 -0
  7. package/dist/{chunk-W66Z3C5H.js → chunk-FJYRWU2V.js} +5 -5
  8. package/dist/chunk-FJYRWU2V.js.map +1 -0
  9. package/dist/{chunk-VXKJ5ZIV.js → chunk-KQ6BLN6W.js} +172 -155
  10. package/dist/chunk-KQ6BLN6W.js.map +1 -0
  11. package/dist/cli.js +6 -6
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{diff-4YFDNEZB.js → diff-2T7UDES7.js} +12 -12
  14. package/dist/diff-2T7UDES7.js.map +1 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/{reset-QVERBAQJ.js → reset-QRXG7KZZ.js} +8 -8
  17. package/dist/reset-QRXG7KZZ.js.map +1 -0
  18. package/dist/{setup-ZSMZ7HZG.js → setup-QUUJ7SH3.js} +8 -8
  19. package/dist/setup-QUUJ7SH3.js.map +1 -0
  20. package/dist/sync-ISBJ7X2T.js +9 -0
  21. package/dist/{upgrade-WILVVHUY.js → upgrade-FALAUUKE.js} +22 -10
  22. package/dist/upgrade-FALAUUKE.js.map +1 -0
  23. package/package.json +15 -14
  24. package/templates/SAFEWORD.md +4 -2
  25. package/templates/commands/cleanup-zombies.md +48 -0
  26. package/templates/guides/zombie-process-cleanup.md +40 -24
  27. package/templates/scripts/cleanup-zombies.sh +222 -0
  28. package/templates/scripts/lint-md.sh +0 -0
  29. package/dist/check-I2J6THGQ.js.map +0 -1
  30. package/dist/chunk-DES5CSPH.js.map +0 -1
  31. package/dist/chunk-DXT6TWW4.js.map +0 -1
  32. package/dist/chunk-VXKJ5ZIV.js.map +0 -1
  33. package/dist/chunk-W66Z3C5H.js.map +0 -1
  34. package/dist/diff-4YFDNEZB.js.map +0 -1
  35. package/dist/reset-QVERBAQJ.js.map +0 -1
  36. package/dist/setup-ZSMZ7HZG.js.map +0 -1
  37. package/dist/sync-VQW5DSTV.js +0 -9
  38. package/dist/upgrade-WILVVHUY.js.map +0 -1
  39. /package/dist/{sync-VQW5DSTV.js.map → sync-ISBJ7X2T.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -8,27 +8,27 @@ import { Command } from "commander";
8
8
  var program = new Command();
9
9
  program.name("safeword").description("CLI for setting up and managing safeword development environments").version(VERSION);
10
10
  program.command("setup").description("Set up safeword in the current project").option("-y, --yes", "Accept all defaults (non-interactive mode)").action(async (options) => {
11
- const { setup } = await import("./setup-ZSMZ7HZG.js");
11
+ const { setup } = await import("./setup-QUUJ7SH3.js");
12
12
  await setup(options);
13
13
  });
14
14
  program.command("check").description("Check project health and versions").option("--offline", "Skip remote version check").action(async (options) => {
15
- const { check } = await import("./check-I2J6THGQ.js");
15
+ const { check } = await import("./check-QMAGWUOA.js");
16
16
  await check(options);
17
17
  });
18
18
  program.command("upgrade").description("Upgrade safeword configuration to latest version").action(async () => {
19
- const { upgrade } = await import("./upgrade-WILVVHUY.js");
19
+ const { upgrade } = await import("./upgrade-FALAUUKE.js");
20
20
  await upgrade();
21
21
  });
22
22
  program.command("diff").description("Preview changes that would be made by upgrade").option("-v, --verbose", "Show full diff output").action(async (options) => {
23
- const { diff } = await import("./diff-4YFDNEZB.js");
23
+ const { diff } = await import("./diff-2T7UDES7.js");
24
24
  await diff(options);
25
25
  });
26
26
  program.command("reset").description("Remove safeword configuration from project").option("-y, --yes", "Skip confirmation prompt").option("--full", "Also remove linting config and uninstall npm packages").action(async (options) => {
27
- const { reset } = await import("./reset-QVERBAQJ.js");
27
+ const { reset } = await import("./reset-QRXG7KZZ.js");
28
28
  await reset(options);
29
29
  });
30
30
  program.command("sync").description("Sync linting plugins with project dependencies").option("-q, --quiet", "Suppress output except errors").option("-s, --stage", "Stage modified files (for pre-commit hooks)").action(async (options) => {
31
- const { sync } = await import("./sync-VQW5DSTV.js");
31
+ const { sync } = await import("./sync-ISBJ7X2T.js");
32
32
  await sync(options);
33
33
  });
34
34
  if (process.argv.length === 2) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { VERSION } from './version.js';\n\nconst program = new Command();\n\nprogram\n .name('safeword')\n .description('CLI for setting up and managing safeword development environments')\n .version(VERSION);\n\nprogram\n .command('setup')\n .description('Set up safeword in the current project')\n .option('-y, --yes', 'Accept all defaults (non-interactive mode)')\n .action(async options => {\n const { setup } = await import('./commands/setup.js');\n await setup(options);\n });\n\nprogram\n .command('check')\n .description('Check project health and versions')\n .option('--offline', 'Skip remote version check')\n .action(async options => {\n const { check } = await import('./commands/check.js');\n await check(options);\n });\n\nprogram\n .command('upgrade')\n .description('Upgrade safeword configuration to latest version')\n .action(async () => {\n const { upgrade } = await import('./commands/upgrade.js');\n await upgrade();\n });\n\nprogram\n .command('diff')\n .description('Preview changes that would be made by upgrade')\n .option('-v, --verbose', 'Show full diff output')\n .action(async options => {\n const { diff } = await import('./commands/diff.js');\n await diff(options);\n });\n\nprogram\n .command('reset')\n .description('Remove safeword configuration from project')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('--full', 'Also remove linting config and uninstall npm packages')\n .action(async options => {\n const { reset } = await import('./commands/reset.js');\n await reset(options);\n });\n\nprogram\n .command('sync')\n .description('Sync linting plugins with project dependencies')\n .option('-q, --quiet', 'Suppress output except errors')\n .option('-s, --stage', 'Stage modified files (for pre-commit hooks)')\n .action(async options => {\n const { sync } = await import('./commands/sync.js');\n await sync(options);\n });\n\n// Show help if no arguments provided\nif (process.argv.length === 2) {\n program.help();\n}\n\n// Parse arguments\nprogram.parse();\n"],"mappings":";;;;;;AAEA,SAAS,eAAe;AAGxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,YAAY,mEAAmE,EAC/E,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,wCAAwC,EACpD,OAAO,aAAa,4CAA4C,EAChE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,mCAAmC,EAC/C,OAAO,aAAa,2BAA2B,EAC/C,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,kDAAkD,EAC9D,OAAO,YAAY;AAClB,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,uBAAuB;AACxD,QAAM,QAAQ;AAChB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,+CAA+C,EAC3D,OAAO,iBAAiB,uBAAuB,EAC/C,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,OAAO;AACpB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,4CAA4C,EACxD,OAAO,aAAa,0BAA0B,EAC9C,OAAO,UAAU,uDAAuD,EACxE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,gDAAgD,EAC5D,OAAO,eAAe,+BAA+B,EACrD,OAAO,eAAe,6CAA6C,EACnE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,OAAO;AACpB,CAAC;AAGH,IAAI,QAAQ,KAAK,WAAW,GAAG;AAC7B,UAAQ,KAAK;AACf;AAGA,QAAQ,MAAM;","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\n\nimport { VERSION } from './version.js';\n\nconst program = new Command();\n\nprogram\n .name('safeword')\n .description('CLI for setting up and managing safeword development environments')\n .version(VERSION);\n\nprogram\n .command('setup')\n .description('Set up safeword in the current project')\n .option('-y, --yes', 'Accept all defaults (non-interactive mode)')\n .action(async options => {\n const { setup } = await import('./commands/setup.js');\n await setup(options);\n });\n\nprogram\n .command('check')\n .description('Check project health and versions')\n .option('--offline', 'Skip remote version check')\n .action(async options => {\n const { check } = await import('./commands/check.js');\n await check(options);\n });\n\nprogram\n .command('upgrade')\n .description('Upgrade safeword configuration to latest version')\n .action(async () => {\n const { upgrade } = await import('./commands/upgrade.js');\n await upgrade();\n });\n\nprogram\n .command('diff')\n .description('Preview changes that would be made by upgrade')\n .option('-v, --verbose', 'Show full diff output')\n .action(async options => {\n const { diff } = await import('./commands/diff.js');\n await diff(options);\n });\n\nprogram\n .command('reset')\n .description('Remove safeword configuration from project')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('--full', 'Also remove linting config and uninstall npm packages')\n .action(async options => {\n const { reset } = await import('./commands/reset.js');\n await reset(options);\n });\n\nprogram\n .command('sync')\n .description('Sync linting plugins with project dependencies')\n .option('-q, --quiet', 'Suppress output except errors')\n .option('-s, --stage', 'Stage modified files (for pre-commit hooks)')\n .action(async options => {\n const { sync } = await import('./commands/sync.js');\n await sync(options);\n });\n\n// Show help if no arguments provided\nif (process.argv.length === 2) {\n program.help();\n}\n\n// Parse arguments\nprogram.parse();\n"],"mappings":";;;;;;AAEA,SAAS,eAAe;AAIxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,YAAY,mEAAmE,EAC/E,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,wCAAwC,EACpD,OAAO,aAAa,4CAA4C,EAChE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,mCAAmC,EAC/C,OAAO,aAAa,2BAA2B,EAC/C,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,kDAAkD,EAC9D,OAAO,YAAY;AAClB,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,uBAAuB;AACxD,QAAM,QAAQ;AAChB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,+CAA+C,EAC3D,OAAO,iBAAiB,uBAAuB,EAC/C,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,OAAO;AACpB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,4CAA4C,EACxD,OAAO,aAAa,0BAA0B,EAC9C,OAAO,UAAU,uDAAuD,EACxE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,gDAAgD,EAC5D,OAAO,eAAe,+BAA+B,EACrD,OAAO,eAAe,6CAA6C,EACnE,OAAO,OAAM,YAAW;AACvB,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,OAAO;AACpB,CAAC;AAGH,IAAI,QAAQ,KAAK,WAAW,GAAG;AAC7B,UAAQ,KAAK;AACf;AAGA,QAAQ,MAAM;","names":[]}
@@ -6,18 +6,18 @@ import {
6
6
  listItem,
7
7
  reconcile,
8
8
  success
9
- } from "./chunk-VXKJ5ZIV.js";
9
+ } from "./chunk-KQ6BLN6W.js";
10
10
  import {
11
11
  SAFEWORD_SCHEMA,
12
12
  exists,
13
13
  readFileSafe
14
- } from "./chunk-DXT6TWW4.js";
14
+ } from "./chunk-CLSGXTOL.js";
15
15
  import {
16
16
  VERSION
17
17
  } from "./chunk-ORQHKDT2.js";
18
18
 
19
19
  // src/commands/diff.ts
20
- import { join } from "path";
20
+ import nodePath from "path";
21
21
  function createUnifiedDiff(oldContent, newContent, filename) {
22
22
  const oldLines = oldContent.split("\n");
23
23
  const newLines = newContent.split("\n");
@@ -93,25 +93,25 @@ function actionsToDiffs(actions, cwd) {
93
93
  if (action.type === "write") {
94
94
  if (seenPaths.has(action.path)) continue;
95
95
  seenPaths.add(action.path);
96
- const fullPath = join(cwd, action.path);
96
+ const fullPath = nodePath.join(cwd, action.path);
97
97
  const currentContent = readFileSafe(fullPath);
98
- if (currentContent === null) {
98
+ if (currentContent === void 0) {
99
99
  diffs.push({
100
100
  path: action.path,
101
101
  status: "added",
102
102
  newContent: action.content
103
103
  });
104
- } else if (currentContent.trim() !== action.content.trim()) {
104
+ } else if (currentContent.trim() === action.content.trim()) {
105
105
  diffs.push({
106
106
  path: action.path,
107
- status: "modified",
107
+ status: "unchanged",
108
108
  currentContent,
109
109
  newContent: action.content
110
110
  });
111
111
  } else {
112
112
  diffs.push({
113
113
  path: action.path,
114
- status: "unchanged",
114
+ status: "modified",
115
115
  currentContent,
116
116
  newContent: action.content
117
117
  });
@@ -122,12 +122,12 @@ function actionsToDiffs(actions, cwd) {
122
122
  }
123
123
  async function diff(options) {
124
124
  const cwd = process.cwd();
125
- const safewordDir = join(cwd, ".safeword");
126
- if (!exists(safewordDir)) {
125
+ const safewordDirectory = nodePath.join(cwd, ".safeword");
126
+ if (!exists(safewordDirectory)) {
127
127
  error("Not configured. Run `safeword setup` first.");
128
128
  process.exit(1);
129
129
  }
130
- const versionPath = join(safewordDir, "version");
130
+ const versionPath = nodePath.join(safewordDirectory, "version");
131
131
  const projectVersion = readFileSafe(versionPath)?.trim() ?? "unknown";
132
132
  header("Safeword Diff");
133
133
  info(`Changes from v${projectVersion} \u2192 v${VERSION}`);
@@ -163,4 +163,4 @@ Packages to install: ${result.packagesToInstall.length}`);
163
163
  export {
164
164
  diff
165
165
  };
166
- //# sourceMappingURL=diff-4YFDNEZB.js.map
166
+ //# sourceMappingURL=diff-2T7UDES7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/diff.ts"],"sourcesContent":["/**\n * Diff command - Preview changes that would be made by upgrade\n *\n * Uses reconcile() with dryRun to compute what would change.\n */\n\nimport nodePath from 'node:path';\n\nimport { type Action, reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { exists, readFileSafe } from '../utils/fs.js';\nimport { error, header, info, listItem, success } from '../utils/output.js';\nimport { VERSION } from '../version.js';\n\nexport interface DiffOptions {\n verbose?: boolean;\n}\n\ninterface FileDiff {\n path: string;\n status: 'added' | 'modified' | 'unchanged';\n currentContent?: string;\n newContent?: string;\n}\n\n/**\n * Create a unified diff between two strings\n * @param oldContent\n * @param newContent\n * @param filename\n */\nfunction createUnifiedDiff(oldContent: string, newContent: string, filename: string): string {\n const oldLines = oldContent.split('\\n');\n const newLines = newContent.split('\\n');\n\n const lines: string[] = [`--- a/${filename}`, `+++ b/${filename}`];\n\n // Simple diff - show all changes\n let hasChanges = false;\n\n const maxLines = Math.max(oldLines.length, newLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const oldLine: string | undefined = oldLines[i];\n const newLine: string | undefined = newLines[i];\n\n if (oldLine === newLine) {\n lines.push(` ${oldLine ?? ''}`);\n } else {\n hasChanges = true;\n if (oldLine !== undefined) {\n lines.push(`-${oldLine}`);\n }\n if (newLine !== undefined) {\n lines.push(`+${newLine}`);\n }\n }\n }\n\n if (!hasChanges) {\n return '';\n }\n\n // Add context marker\n lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);\n\n return lines.join('\\n');\n}\n\n/**\n * List files by category\n * @param categoryName\n * @param files\n */\nfunction listFileCategory(categoryName: string, files: FileDiff[]): void {\n if (files.length === 0) return;\n info(`\\n${categoryName}:`);\n for (const file of files) {\n listItem(file.path);\n }\n}\n\n/**\n * Show verbose diff output for modified files\n * @param files\n */\nfunction showModifiedDiffs(files: FileDiff[]): void {\n for (const file of files) {\n if (!file.currentContent || !file.newContent) continue;\n info(`\\n${file.path}:`);\n const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);\n if (diffOutput) {\n console.log(diffOutput);\n }\n }\n}\n\n/**\n * Show verbose output for added files (truncated preview)\n * @param files\n */\nfunction showAddedPreviews(files: FileDiff[]): void {\n for (const file of files) {\n if (!file.newContent) continue;\n info(`\\n${file.path}: (new file)`);\n const allLines = file.newContent.split('\\n');\n const lines = allLines.slice(0, 10);\n for (const line of lines) {\n console.log(`+${line}`);\n }\n if (allLines.length > 10) {\n console.log('... (truncated)');\n }\n }\n}\n\n/**\n * Show packages to install\n * @param packages\n */\nfunction showPackagesToInstall(packages: string[]): void {\n if (packages.length === 0) return;\n info('\\nPackages to install:');\n for (const pkg of packages) {\n listItem(pkg);\n }\n}\n\n/**\n * Convert reconcile actions to file diffs\n * @param actions\n * @param cwd\n */\nfunction actionsToDiffs(actions: Action[], cwd: string): FileDiff[] {\n const diffs: FileDiff[] = [];\n const seenPaths = new Set<string>();\n\n for (const action of actions) {\n if (action.type === 'write') {\n if (seenPaths.has(action.path)) continue;\n seenPaths.add(action.path);\n\n const fullPath = nodePath.join(cwd, action.path);\n const currentContent = readFileSafe(fullPath);\n\n if (currentContent === undefined) {\n diffs.push({\n path: action.path,\n status: 'added',\n newContent: action.content,\n });\n } else if (currentContent.trim() === action.content.trim()) {\n diffs.push({\n path: action.path,\n status: 'unchanged',\n currentContent,\n newContent: action.content,\n });\n } else {\n diffs.push({\n path: action.path,\n status: 'modified',\n currentContent,\n newContent: action.content,\n });\n }\n }\n }\n\n return diffs;\n}\n\n/**\n *\n * @param options\n */\nexport async function diff(options: DiffOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDirectory)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = nodePath.join(safewordDirectory, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? 'unknown';\n\n header('Safeword Diff');\n info(`Changes from v${projectVersion} → v${VERSION}`);\n\n // Use reconcile with dryRun to compute changes\n const ctx = createProjectContext(cwd);\n const result = await reconcile(SAFEWORD_SCHEMA, 'upgrade', ctx, { dryRun: true });\n\n // Convert actions to file diffs\n const diffs = actionsToDiffs(result.actions, cwd);\n\n const added = diffs.filter(d => d.status === 'added');\n const modified = diffs.filter(d => d.status === 'modified');\n const unchanged = diffs.filter(d => d.status === 'unchanged');\n\n // Summary\n info(\n `\\nSummary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`,\n );\n\n // Package changes\n if (result.packagesToInstall.length > 0) {\n info(`\\nPackages to install: ${result.packagesToInstall.length}`);\n }\n\n // List by category\n listFileCategory('Added', added);\n listFileCategory('Modified', modified);\n listFileCategory('Unchanged', unchanged);\n\n // Verbose output - show actual diffs\n if (options.verbose) {\n header('Detailed Changes');\n showModifiedDiffs(modified);\n showAddedPreviews(added);\n showPackagesToInstall(result.packagesToInstall);\n }\n\n if (added.length === 0 && modified.length === 0 && result.packagesToInstall.length === 0) {\n success('\\nNo changes needed - configuration is up to date');\n } else {\n info('\\nRun `safeword upgrade` to apply these changes');\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- declare const VERSION: string;
2
-
3
1
  interface ProjectType {
4
2
  typescript: boolean;
5
3
  react: boolean;
@@ -17,4 +15,6 @@ interface ProjectType {
17
15
  shell: boolean;
18
16
  }
19
17
 
18
+ declare const VERSION: string;
19
+
20
20
  export { type ProjectType, VERSION };
@@ -7,20 +7,20 @@ import {
7
7
  reconcile,
8
8
  success,
9
9
  warn
10
- } from "./chunk-VXKJ5ZIV.js";
10
+ } from "./chunk-KQ6BLN6W.js";
11
11
  import {
12
12
  SAFEWORD_SCHEMA,
13
13
  exists
14
- } from "./chunk-DXT6TWW4.js";
14
+ } from "./chunk-CLSGXTOL.js";
15
15
  import "./chunk-ORQHKDT2.js";
16
16
 
17
17
  // src/commands/reset.ts
18
- import { join } from "path";
19
18
  import { execSync } from "child_process";
19
+ import nodePath from "path";
20
20
  async function reset(options) {
21
21
  const cwd = process.cwd();
22
- const safewordDir = join(cwd, ".safeword");
23
- if (!exists(safewordDir)) {
22
+ const safewordDirectory = nodePath.join(cwd, ".safeword");
23
+ if (!exists(safewordDirectory)) {
24
24
  info("Nothing to remove. Project is not configured with safeword.");
25
25
  return;
26
26
  }
@@ -63,12 +63,12 @@ async function reset(options) {
63
63
  listItem("devDependencies (eslint, prettier, husky, lint-staged, etc.)");
64
64
  }
65
65
  success("\nSafeword configuration removed");
66
- } catch (err) {
67
- error(`Reset failed: ${err instanceof Error ? err.message : "Unknown error"}`);
66
+ } catch (error_) {
67
+ error(`Reset failed: ${error_ instanceof Error ? error_.message : "Unknown error"}`);
68
68
  process.exit(1);
69
69
  }
70
70
  }
71
71
  export {
72
72
  reset
73
73
  };
74
- //# sourceMappingURL=reset-QVERBAQJ.js.map
74
+ //# sourceMappingURL=reset-QRXG7KZZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/reset.ts"],"sourcesContent":["/**\n * Reset command - Remove safeword configuration from project\n *\n * Uses reconcile() with mode='uninstall' or 'uninstall-full' to remove configuration.\n *\n * By default, preserves linting configuration (eslint, prettier, etc.)\n * Use --full to also remove linting config and uninstall npm packages\n */\n\nimport { execSync } from 'node:child_process';\nimport nodePath from 'node:path';\n\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { exists } from '../utils/fs.js';\nimport { error, header, info, listItem, success, warn } from '../utils/output.js';\n\nexport interface ResetOptions {\n yes?: boolean;\n full?: boolean;\n}\n\n/**\n *\n * @param options\n */\nexport async function reset(options: ResetOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDirectory)) {\n info('Nothing to remove. Project is not configured with safeword.');\n return;\n }\n\n const fullReset = options.full ?? false;\n\n header('Safeword Reset');\n if (fullReset) {\n info('Performing full reset (including linting configuration)...');\n } else {\n info('Removing safeword configuration...');\n }\n\n try {\n // Use reconcile with appropriate mode\n const mode = fullReset ? 'uninstall-full' : 'uninstall';\n const ctx = createProjectContext(cwd);\n const result = await reconcile(SAFEWORD_SCHEMA, mode, ctx);\n\n // Handle npm uninstall for full reset\n if (fullReset && result.packagesToRemove.length > 0) {\n info('\\nUninstalling devDependencies...');\n\n try {\n const uninstallCmd = `npm uninstall ${result.packagesToRemove.join(' ')}`;\n info(`Running: ${uninstallCmd}`);\n execSync(uninstallCmd, { cwd, stdio: 'inherit' });\n success('Uninstalled safeword devDependencies');\n } catch {\n warn('Failed to uninstall some packages. Run manually:');\n listItem(`npm uninstall ${result.packagesToRemove.join(' ')}`);\n }\n }\n\n // Print summary\n header('Reset Complete');\n\n if (result.removed.length > 0) {\n info('\\nRemoved:');\n for (const item of result.removed) {\n listItem(item);\n }\n }\n\n // Note about preserved linting (only shown if not full reset)\n if (!fullReset) {\n info('\\nPreserved (use --full to remove):');\n listItem('eslint.config.mjs');\n listItem('.prettierrc');\n listItem('.markdownlint-cli2.jsonc');\n listItem('package.json (scripts, lint-staged config)');\n listItem('devDependencies (eslint, prettier, husky, lint-staged, etc.)');\n }\n\n success('\\nSafeword configuration removed');\n } catch (error_) {\n error(`Reset failed: ${error_ instanceof Error ? error_.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AASA,SAAS,gBAAgB;AACzB,OAAO,cAAc;AAiBrB,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,oBAAoB,SAAS,KAAK,KAAK,WAAW;AAGxD,MAAI,CAAC,OAAO,iBAAiB,GAAG;AAC9B,SAAK,6DAA6D;AAClE;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,QAAQ;AAElC,SAAO,gBAAgB;AACvB,MAAI,WAAW;AACb,SAAK,4DAA4D;AAAA,EACnE,OAAO;AACL,SAAK,oCAAoC;AAAA,EAC3C;AAEA,MAAI;AAEF,UAAM,OAAO,YAAY,mBAAmB;AAC5C,UAAM,MAAM,qBAAqB,GAAG;AACpC,UAAM,SAAS,MAAM,UAAU,iBAAiB,MAAM,GAAG;AAGzD,QAAI,aAAa,OAAO,iBAAiB,SAAS,GAAG;AACnD,WAAK,mCAAmC;AAExC,UAAI;AACF,cAAM,eAAe,iBAAiB,OAAO,iBAAiB,KAAK,GAAG,CAAC;AACvE,aAAK,YAAY,YAAY,EAAE;AAC/B,iBAAS,cAAc,EAAE,KAAK,OAAO,UAAU,CAAC;AAChD,gBAAQ,sCAAsC;AAAA,MAChD,QAAQ;AACN,aAAK,kDAAkD;AACvD,iBAAS,iBAAiB,OAAO,iBAAiB,KAAK,GAAG,CAAC,EAAE;AAAA,MAC/D;AAAA,IACF;AAGA,WAAO,gBAAgB;AAEvB,QAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,WAAK,YAAY;AACjB,iBAAW,QAAQ,OAAO,SAAS;AACjC,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAGA,QAAI,CAAC,WAAW;AACd,WAAK,qCAAqC;AAC1C,eAAS,mBAAmB;AAC5B,eAAS,aAAa;AACtB,eAAS,0BAA0B;AACnC,eAAS,4CAA4C;AACrD,eAAS,8DAA8D;AAAA,IACzE;AAEA,YAAQ,kCAAkC;AAAA,EAC5C,SAAS,QAAQ;AACf,UAAM,iBAAiB,kBAAkB,QAAQ,OAAO,UAAU,eAAe,EAAE;AACnF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
@@ -8,19 +8,19 @@ import {
8
8
  reconcile,
9
9
  success,
10
10
  warn
11
- } from "./chunk-VXKJ5ZIV.js";
11
+ } from "./chunk-KQ6BLN6W.js";
12
12
  import {
13
13
  SAFEWORD_SCHEMA,
14
14
  exists,
15
15
  writeJson
16
- } from "./chunk-DXT6TWW4.js";
16
+ } from "./chunk-CLSGXTOL.js";
17
17
  import {
18
18
  VERSION
19
19
  } from "./chunk-ORQHKDT2.js";
20
20
 
21
21
  // src/commands/setup.ts
22
- import { join, basename } from "path";
23
22
  import { execSync } from "child_process";
23
+ import nodePath from "path";
24
24
  function printChangesSummary(created, updated, packageJsonCreated) {
25
25
  if (created.length > 0 || packageJsonCreated) {
26
26
  info("\nCreated:");
@@ -34,15 +34,15 @@ function printChangesSummary(created, updated, packageJsonCreated) {
34
34
  }
35
35
  async function setup(options) {
36
36
  const cwd = process.cwd();
37
- const safewordDir = join(cwd, ".safeword");
38
- if (exists(safewordDir)) {
37
+ const safewordDirectory = nodePath.join(cwd, ".safeword");
38
+ if (exists(safewordDirectory)) {
39
39
  error("Already configured. Run `safeword upgrade` to update.");
40
40
  process.exit(1);
41
41
  }
42
- const packageJsonPath = join(cwd, "package.json");
42
+ const packageJsonPath = nodePath.join(cwd, "package.json");
43
43
  let packageJsonCreated = false;
44
44
  if (!exists(packageJsonPath)) {
45
- const dirName = basename(cwd) || "project";
45
+ const dirName = nodePath.basename(cwd) || "project";
46
46
  const defaultPackageJson = {
47
47
  name: dirName,
48
48
  version: "0.1.0",
@@ -97,4 +97,4 @@ Safeword ${VERSION} installed successfully!`);
97
97
  export {
98
98
  setup
99
99
  };
100
- //# sourceMappingURL=setup-ZSMZ7HZG.js.map
100
+ //# sourceMappingURL=setup-QUUJ7SH3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/setup.ts"],"sourcesContent":["/**\n * Setup command - Initialize safeword in a project\n *\n * Uses reconcile() with mode='install' to create all managed files.\n */\n\nimport { execSync } from 'node:child_process';\nimport nodePath from 'node:path';\n\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { exists, writeJson } from '../utils/fs.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { error, header, info, listItem, success, warn } from '../utils/output.js';\nimport { VERSION } from '../version.js';\n\nexport interface SetupOptions {\n yes?: boolean;\n}\n\ninterface PackageJson {\n name?: string;\n version?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n 'lint-staged'?: Record<string, string[]>;\n}\n\n/**\n * Print file changes summary\n * @param created\n * @param updated\n * @param packageJsonCreated\n */\nfunction printChangesSummary(\n created: string[],\n updated: string[],\n packageJsonCreated: boolean,\n): void {\n if (created.length > 0 || packageJsonCreated) {\n info('\\nCreated:');\n if (packageJsonCreated) listItem('package.json');\n for (const file of created) listItem(file);\n }\n\n if (updated.length > 0) {\n info('\\nModified:');\n for (const file of updated) listItem(file);\n }\n}\n\n/**\n *\n * @param options\n */\nexport async function setup(options: SetupOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if already configured\n if (exists(safewordDirectory)) {\n error('Already configured. Run `safeword upgrade` to update.');\n process.exit(1);\n }\n\n // Check for package.json, create if missing\n const packageJsonPath = nodePath.join(cwd, 'package.json');\n let packageJsonCreated = false;\n if (!exists(packageJsonPath)) {\n const dirName = nodePath.basename(cwd) || 'project';\n const defaultPackageJson: PackageJson = {\n name: dirName,\n version: '0.1.0',\n scripts: {},\n };\n writeJson(packageJsonPath, defaultPackageJson);\n packageJsonCreated = true;\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n header('Safeword Setup');\n info(`Version: ${VERSION}`);\n\n if (packageJsonCreated) {\n info('Created package.json (none found)');\n }\n\n try {\n // Use reconcile with mode='install' to create all managed files\n info('\\nCreating safeword configuration...');\n const ctx = createProjectContext(cwd);\n const result = await reconcile(SAFEWORD_SCHEMA, 'install', ctx);\n\n success('Created .safeword directory and configuration');\n\n // Install npm dependencies\n if (result.packagesToInstall.length > 0) {\n info('\\nInstalling linting dependencies...');\n\n try {\n const installCmd = `npm install -D ${result.packagesToInstall.join(' ')}`;\n info(`Running: ${installCmd}`);\n\n execSync(installCmd, { cwd, stdio: 'inherit' });\n success('Installed linting dependencies');\n } catch {\n warn('Failed to install dependencies. Run manually:');\n listItem(`npm install -D ${result.packagesToInstall.join(' ')}`);\n }\n }\n\n // Report Husky status\n if (!isGitRepo(cwd)) {\n if (isNonInteractive) {\n warn('Skipped Husky setup (no git repository)');\n } else {\n warn('Skipped Husky setup (no .git directory)');\n info('Initialize git and run safeword upgrade to enable pre-commit hooks');\n }\n }\n\n // Print summary\n header('Setup Complete');\n printChangesSummary(result.created, result.updated, packageJsonCreated);\n\n info('\\nNext steps:');\n listItem('Run `safeword check` to verify setup');\n listItem('Commit the new files to git');\n\n success(`\\nSafeword ${VERSION} installed successfully!`);\n } catch (error_) {\n error(`Setup failed: ${error_ instanceof Error ? error_.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAMA,SAAS,gBAAgB;AACzB,OAAO,cAAc;AA6BrB,SAAS,oBACP,SACA,SACA,oBACM;AACN,MAAI,QAAQ,SAAS,KAAK,oBAAoB;AAC5C,SAAK,YAAY;AACjB,QAAI,mBAAoB,UAAS,cAAc;AAC/C,eAAW,QAAQ,QAAS,UAAS,IAAI;AAAA,EAC3C;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,SAAK,aAAa;AAClB,eAAW,QAAQ,QAAS,UAAS,IAAI;AAAA,EAC3C;AACF;AAMA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,oBAAoB,SAAS,KAAK,KAAK,WAAW;AAGxD,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,uDAAuD;AAC7D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,kBAAkB,SAAS,KAAK,KAAK,cAAc;AACzD,MAAI,qBAAqB;AACzB,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,UAAM,UAAU,SAAS,SAAS,GAAG,KAAK;AAC1C,UAAM,qBAAkC;AAAA,MACtC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,CAAC;AAAA,IACZ;AACA,cAAU,iBAAiB,kBAAkB;AAC7C,yBAAqB;AAAA,EACvB;AAEA,QAAM,mBAAmB,QAAQ,OAAO,CAAC,QAAQ,MAAM;AAEvD,SAAO,gBAAgB;AACvB,OAAK,YAAY,OAAO,EAAE;AAE1B,MAAI,oBAAoB;AACtB,SAAK,mCAAmC;AAAA,EAC1C;AAEA,MAAI;AAEF,SAAK,sCAAsC;AAC3C,UAAM,MAAM,qBAAqB,GAAG;AACpC,UAAM,SAAS,MAAM,UAAU,iBAAiB,WAAW,GAAG;AAE9D,YAAQ,+CAA+C;AAGvD,QAAI,OAAO,kBAAkB,SAAS,GAAG;AACvC,WAAK,sCAAsC;AAE3C,UAAI;AACF,cAAM,aAAa,kBAAkB,OAAO,kBAAkB,KAAK,GAAG,CAAC;AACvE,aAAK,YAAY,UAAU,EAAE;AAE7B,iBAAS,YAAY,EAAE,KAAK,OAAO,UAAU,CAAC;AAC9C,gBAAQ,gCAAgC;AAAA,MAC1C,QAAQ;AACN,aAAK,+CAA+C;AACpD,iBAAS,kBAAkB,OAAO,kBAAkB,KAAK,GAAG,CAAC,EAAE;AAAA,MACjE;AAAA,IACF;AAGA,QAAI,CAAC,UAAU,GAAG,GAAG;AACnB,UAAI,kBAAkB;AACpB,aAAK,yCAAyC;AAAA,MAChD,OAAO;AACL,aAAK,yCAAyC;AAC9C,aAAK,oEAAoE;AAAA,MAC3E;AAAA,IACF;AAGA,WAAO,gBAAgB;AACvB,wBAAoB,OAAO,SAAS,OAAO,SAAS,kBAAkB;AAEtE,SAAK,eAAe;AACpB,aAAS,sCAAsC;AAC/C,aAAS,6BAA6B;AAEtC,YAAQ;AAAA,WAAc,OAAO,0BAA0B;AAAA,EACzD,SAAS,QAAQ;AACf,UAAM,iBAAiB,kBAAkB,QAAQ,OAAO,UAAU,eAAe,EAAE;AACnF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ sync
3
+ } from "./chunk-4URRFBUS.js";
4
+ import "./chunk-CLSGXTOL.js";
5
+ import "./chunk-ORQHKDT2.js";
6
+ export {
7
+ sync
8
+ };
9
+ //# sourceMappingURL=sync-ISBJ7X2T.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  compareVersions
3
- } from "./chunk-W66Z3C5H.js";
3
+ } from "./chunk-FJYRWU2V.js";
4
4
  import {
5
5
  sync
6
- } from "./chunk-DES5CSPH.js";
6
+ } from "./chunk-4URRFBUS.js";
7
7
  import {
8
8
  createProjectContext,
9
9
  error,
@@ -11,27 +11,38 @@ import {
11
11
  info,
12
12
  listItem,
13
13
  reconcile,
14
- success
15
- } from "./chunk-VXKJ5ZIV.js";
14
+ success,
15
+ warn
16
+ } from "./chunk-KQ6BLN6W.js";
16
17
  import {
17
18
  SAFEWORD_SCHEMA,
18
19
  exists,
19
20
  readFileSafe
20
- } from "./chunk-DXT6TWW4.js";
21
+ } from "./chunk-CLSGXTOL.js";
21
22
  import {
22
23
  VERSION
23
24
  } from "./chunk-ORQHKDT2.js";
24
25
 
25
26
  // src/commands/upgrade.ts
26
- import { join } from "path";
27
+ import nodePath from "path";
28
+ function printDeprecatedPackagesInfo(packages) {
29
+ if (packages.length === 0) return;
30
+ warn(`
31
+ ${packages.length} package(s) are now bundled in eslint-plugin-safeword:`);
32
+ for (const pkg of packages) {
33
+ listItem(pkg);
34
+ }
35
+ info("\nIf you don't use these elsewhere, you can remove them:");
36
+ listItem(`npm uninstall ${packages.join(" ")}`);
37
+ }
27
38
  async function upgrade() {
28
39
  const cwd = process.cwd();
29
- const safewordDir = join(cwd, ".safeword");
30
- if (!exists(safewordDir)) {
40
+ const safewordDirectory = nodePath.join(cwd, ".safeword");
41
+ if (!exists(safewordDirectory)) {
31
42
  error("Not configured. Run `safeword setup` first.");
32
43
  process.exit(1);
33
44
  }
34
- const versionPath = join(safewordDir, "version");
45
+ const versionPath = nodePath.join(safewordDirectory, "version");
35
46
  const projectVersion = readFileSafe(versionPath)?.trim() ?? "0.0.0";
36
47
  if (compareVersions(VERSION, projectVersion) < 0) {
37
48
  error(`CLI v${VERSION} is older than project v${projectVersion}.`);
@@ -63,6 +74,7 @@ Version: v${projectVersion} \u2192 v${VERSION}`);
63
74
  Syncing ${result.packagesToInstall.length} package(s)...`);
64
75
  await sync();
65
76
  }
77
+ printDeprecatedPackagesInfo(result.packagesToRemove);
66
78
  success(`
67
79
  Safeword upgraded to v${VERSION}`);
68
80
  } catch (error_) {
@@ -73,4 +85,4 @@ Safeword upgraded to v${VERSION}`);
73
85
  export {
74
86
  upgrade
75
87
  };
76
- //# sourceMappingURL=upgrade-WILVVHUY.js.map
88
+ //# sourceMappingURL=upgrade-FALAUUKE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Upgrade command - Update safeword configuration to latest version\n *\n * Uses reconcile() with mode='upgrade' to update all managed files.\n */\n\nimport nodePath from 'node:path';\n\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { exists, readFileSafe } from '../utils/fs.js';\nimport { error, header, info, listItem, success, warn } from '../utils/output.js';\nimport { compareVersions } from '../utils/version.js';\nimport { VERSION } from '../version.js';\nimport { sync } from './sync.js';\n\n/**\n * Print info about deprecated packages that can be removed.\n * Does NOT auto-remove because user may have installed these independently.\n * @param packages - List of package names that are deprecated\n */\nfunction printDeprecatedPackagesInfo(packages: string[]): void {\n if (packages.length === 0) return;\n\n warn(`\\n${packages.length} package(s) are now bundled in eslint-plugin-safeword:`);\n for (const pkg of packages) {\n listItem(pkg);\n }\n info(\"\\nIf you don't use these elsewhere, you can remove them:\");\n listItem(`npm uninstall ${packages.join(' ')}`);\n}\n\n/**\n * Upgrade safeword configuration to the latest version.\n */\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDirectory = nodePath.join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDirectory)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = nodePath.join(safewordDirectory, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? '0.0.0';\n\n // Check for downgrade\n if (compareVersions(VERSION, projectVersion) < 0) {\n error(`CLI v${VERSION} is older than project v${projectVersion}.`);\n error('Update the CLI first: npm install -g safeword');\n process.exit(1);\n }\n\n header('Safeword Upgrade');\n info(`Upgrading from v${projectVersion} to v${VERSION}`);\n\n try {\n // Use reconcile with mode='upgrade' to update all managed files\n const ctx = createProjectContext(cwd);\n const result = await reconcile(SAFEWORD_SCHEMA, 'upgrade', ctx);\n\n // Print summary\n header('Upgrade Complete');\n\n info(`\\nVersion: v${projectVersion} → v${VERSION}`);\n\n if (result.created.length > 0) {\n info('\\nCreated:');\n for (const file of result.created) {\n listItem(file);\n }\n }\n\n if (result.updated.length > 0) {\n info('\\nUpdated:');\n for (const file of result.updated) {\n listItem(file);\n }\n }\n\n // Auto-sync: install missing ESLint packages\n if (result.packagesToInstall.length > 0) {\n info(`\\nSyncing ${result.packagesToInstall.length} package(s)...`);\n await sync();\n }\n\n // Notify about deprecated packages (user may remove manually)\n printDeprecatedPackagesInfo(result.packagesToRemove);\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (error_) {\n error(`Upgrade failed: ${error_ instanceof Error ? error_.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,OAAO,cAAc;AAgBrB,SAAS,4BAA4B,UAA0B;AAC7D,MAAI,SAAS,WAAW,EAAG;AAE3B,OAAK;AAAA,EAAK,SAAS,MAAM,wDAAwD;AACjF,aAAW,OAAO,UAAU;AAC1B,aAAS,GAAG;AAAA,EACd;AACA,OAAK,0DAA0D;AAC/D,WAAS,iBAAiB,SAAS,KAAK,GAAG,CAAC,EAAE;AAChD;AAKA,eAAsB,UAAyB;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,oBAAoB,SAAS,KAAK,KAAK,WAAW;AAGxD,MAAI,CAAC,OAAO,iBAAiB,GAAG;AAC9B,UAAM,6CAA6C;AACnD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,SAAS,KAAK,mBAAmB,SAAS;AAC9D,QAAM,iBAAiB,aAAa,WAAW,GAAG,KAAK,KAAK;AAG5D,MAAI,gBAAgB,SAAS,cAAc,IAAI,GAAG;AAChD,UAAM,QAAQ,OAAO,2BAA2B,cAAc,GAAG;AACjE,UAAM,+CAA+C;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,kBAAkB;AACzB,OAAK,mBAAmB,cAAc,QAAQ,OAAO,EAAE;AAEvD,MAAI;AAEF,UAAM,MAAM,qBAAqB,GAAG;AACpC,UAAM,SAAS,MAAM,UAAU,iBAAiB,WAAW,GAAG;AAG9D,WAAO,kBAAkB;AAEzB,SAAK;AAAA,YAAe,cAAc,YAAO,OAAO,EAAE;AAElD,QAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,WAAK,YAAY;AACjB,iBAAW,QAAQ,OAAO,SAAS;AACjC,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,WAAK,YAAY;AACjB,iBAAW,QAAQ,OAAO,SAAS;AACjC,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAGA,QAAI,OAAO,kBAAkB,SAAS,GAAG;AACvC,WAAK;AAAA,UAAa,OAAO,kBAAkB,MAAM,gBAAgB;AACjE,YAAM,KAAK;AAAA,IACb;AAGA,gCAA4B,OAAO,gBAAgB;AAEnD,YAAQ;AAAA,wBAA2B,OAAO,EAAE;AAAA,EAC9C,SAAS,QAAQ;AACf,UAAM,mBAAmB,kBAAkB,QAAQ,OAAO,UAAU,eAAe,EAAE;AACrF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safeword",
3
- "version": "0.8.6",
3
+ "version": "0.8.9",
4
4
  "description": "CLI for setting up and managing safeword development environments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,6 +19,18 @@
19
19
  "engines": {
20
20
  "node": ">=18"
21
21
  },
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "dev": "tsup --watch",
25
+ "test": "vitest run",
26
+ "test:integration": "vitest run tests/integration/",
27
+ "test:watch": "vitest",
28
+ "test:coverage": "vitest run --coverage",
29
+ "typecheck": "tsc --noEmit",
30
+ "lint": "eslint src tests",
31
+ "clean": "rm -rf dist",
32
+ "prepublishOnly": "npm run build && npm test"
33
+ },
22
34
  "dependencies": {
23
35
  "commander": "^12.1.0"
24
36
  },
@@ -36,16 +48,5 @@
36
48
  "claude-code"
37
49
  ],
38
50
  "author": "",
39
- "license": "MIT",
40
- "scripts": {
41
- "build": "tsup",
42
- "dev": "tsup --watch",
43
- "test": "vitest run",
44
- "test:e2e": "vitest run tests/e2e/",
45
- "test:watch": "vitest",
46
- "test:coverage": "vitest run --coverage",
47
- "typecheck": "tsc --noEmit",
48
- "lint": "eslint src tests",
49
- "clean": "rm -rf dist"
50
- }
51
- }
51
+ "license": "MIT"
52
+ }
@@ -311,7 +311,9 @@ When markdown lint reports MD040 (missing language), choose:
311
311
 
312
312
  1. **Clarity → Simplicity → Correctness** (in that order)
313
313
  2. **Test what you can test**—never ask user to verify
314
- 3. **RED → GREEN → REFACTOR**—never skip steps
314
+ 3. **ALWAYS USE STRICT TDD: RED → GREEN → REFACTOR**—never skip steps
315
315
  4. **Commit after each GREEN phase**
316
316
  5. **Read the matching guide** when a trigger fires
317
- 6. **End every response** with: `{"proposedChanges": bool, "madeChanges": bool, "askedQuestion": bool}`
317
+ 6. **Always read the latest documentation for the relevant tool**
318
+ 7. **AVOID BLOAT**
319
+ 8. **End every response** with: `{"proposedChanges": bool, "madeChanges": bool, "askedQuestion": bool}`
@@ -0,0 +1,48 @@
1
+ ---
2
+ description: Kill zombie dev servers and test processes for this project
3
+ ---
4
+
5
+ # Cleanup Zombies
6
+
7
+ Kill zombie processes (dev servers, Playwright browsers, test runners) for the current project only. Safe to use in multi-project environments.
8
+
9
+ ## Instructions
10
+
11
+ Run the cleanup script with `--dry-run` first to preview what will be killed:
12
+
13
+ ```bash
14
+ ./.safeword/scripts/cleanup-zombies.sh --dry-run
15
+ ```
16
+
17
+ If the output looks correct, run without `--dry-run`:
18
+
19
+ ```bash
20
+ ./.safeword/scripts/cleanup-zombies.sh
21
+ ```
22
+
23
+ ## What It Does
24
+
25
+ 1. **Auto-detects framework** - Finds port from vite.config.ts, next.config.js, etc. (checks root, `packages/*/`, `apps/*/` for monorepos)
26
+ 2. **Kills by port** - Dev server port AND test port (port + 1000)
27
+ 3. **Kills test processes** - Playwright, Chromium, Electron (scoped to this project)
28
+ 4. **Multi-project safe** - Only kills processes matching this project's directory
29
+
30
+ ## Manual Override
31
+
32
+ If auto-detection fails or you need a specific port:
33
+
34
+ ```bash
35
+ # Explicit port
36
+ ./.safeword/scripts/cleanup-zombies.sh 5173
37
+
38
+ # Port + additional pattern
39
+ ./.safeword/scripts/cleanup-zombies.sh 5173 "electron"
40
+ ```
41
+
42
+ ## When to Use
43
+
44
+ - Port already in use when starting dev server
45
+ - Tests hanging or failing due to zombie processes
46
+ - Switching between projects
47
+ - Before running E2E tests
48
+ - After interrupted test runs
@@ -4,6 +4,24 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## Quick Start
8
+
9
+ Use the built-in cleanup script:
10
+
11
+ ```bash
12
+ # Preview what would be killed (safe)
13
+ ./.safeword/scripts/cleanup-zombies.sh --dry-run
14
+
15
+ # Kill zombie processes
16
+ ./.safeword/scripts/cleanup-zombies.sh
17
+ ```
18
+
19
+ The script auto-detects your framework (Vite, Next.js, etc.) and kills only processes belonging to this project.
20
+
21
+ For manual control or debugging, see the detailed sections below.
22
+
23
+ ---
24
+
7
25
  ## The Problem
8
26
 
9
27
  When running dev servers and E2E tests across multiple projects, zombie processes accumulate:
@@ -52,35 +70,32 @@ sleep 2
52
70
 
53
71
  ---
54
72
 
55
- ## Project-Specific Cleanup Script
73
+ ## Built-in Cleanup Script
56
74
 
57
- For frequent cleanup needs, create `scripts/cleanup.sh` in each project:
75
+ Safeword includes a cleanup script at `.safeword/scripts/cleanup-zombies.sh`:
58
76
 
59
77
  ```bash
60
- #!/bin/bash
61
- # scripts/cleanup.sh - Kill only THIS project's processes
62
-
63
- DEV_PORT=3000 # Dev server port (change per project)
64
- TEST_PORT=$((DEV_PORT + 1000)) # Test server port (Playwright managed)
65
- PROJECT_DIR="$(pwd)"
78
+ # Auto-detect framework and clean up
79
+ ./.safeword/scripts/cleanup-zombies.sh
66
80
 
67
- echo "Cleaning up $PROJECT_DIR (dev: $DEV_PORT, test: $TEST_PORT)..."
81
+ # Preview first (recommended)
82
+ ./.safeword/scripts/cleanup-zombies.sh --dry-run
68
83
 
69
- # Kill both dev and test servers by port
70
- lsof -ti:$DEV_PORT -ti:$TEST_PORT | xargs kill -9 2>/dev/null
84
+ # Explicit port override
85
+ ./.safeword/scripts/cleanup-zombies.sh 5173
71
86
 
72
- # Kill Playwright browsers for this project
73
- ps aux | grep -E "(playwright|chromium)" | grep "$PROJECT_DIR" | grep -v grep | awk '{print $2}' | xargs kill -9 2>/dev/null
74
-
75
- # Kill test runners
76
- pgrep -f "playwright test.*$(basename $PROJECT_DIR)" | xargs kill -9 2>/dev/null
77
-
78
- echo "Cleanup complete!"
87
+ # Port + additional pattern
88
+ ./.safeword/scripts/cleanup-zombies.sh 5173 "electron"
79
89
  ```
80
90
 
81
- **Make executable:** `chmod +x scripts/cleanup.sh`
91
+ **Features:**
92
+
93
+ - Auto-detects port from config files (vite.config.ts, next.config.js, etc.)
94
+ - Kills dev port AND test port (port + 1000)
95
+ - Scopes all pattern matching to current project directory
96
+ - `--dry-run` shows what would be killed without killing
82
97
 
83
- **Usage:** `./scripts/cleanup.sh`
98
+ **Supported frameworks:** Vite, Next.js, Nuxt, SvelteKit, Astro, Angular
84
99
 
85
100
  ---
86
101
 
@@ -197,9 +212,10 @@ ps aux | grep "/Users/alex/projects/my-project"
197
212
 
198
213
  | Situation | Command |
199
214
  | ---------------------------------------- | ---------------------------------------------------------------- |
215
+ | Quick cleanup (recommended) | `./.safeword/scripts/cleanup-zombies.sh` |
216
+ | Preview before killing | `./.safeword/scripts/cleanup-zombies.sh --dry-run` |
200
217
  | Kill dev + test servers (use your ports) | `lsof -ti:$DEV_PORT -ti:$TEST_PORT \| xargs kill -9 2>/dev/null` |
201
218
  | Kill Playwright (this project) | `pkill -f "playwright.*$(pwd)"` |
202
- | Kill all for this project | `./scripts/cleanup.sh` |
203
219
  | Check what's on port | `lsof -i:3000` |
204
220
  | Find zombie processes | `ps aux \| grep -E "(node\|playwright\|chromium)"` |
205
221
  | Preview what `pkill -f` would kill | `pgrep -f "pattern"` (verify before running pkill) |
@@ -224,7 +240,7 @@ ps aux | grep "/Users/alex/projects/my-project"
224
240
 
225
241
  ## Key Takeaways
226
242
 
227
- - Use port-based cleanup, not `killall node` (affects all projects)
228
- - Filter by project directory: `pkill -f "playwright.*$(pwd)"`
243
+ - Use `./.safeword/scripts/cleanup-zombies.sh` for quick, safe cleanup
244
+ - Always preview with `--dry-run` first when unsure
245
+ - Never use `killall node` (affects all projects)
229
246
  - Clean up before AND after development sessions
230
- - Create project-specific `scripts/cleanup.sh` for consistent cleanup