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.
- package/dist/{check-I2J6THGQ.js → check-QMAGWUOA.js} +24 -22
- package/dist/check-QMAGWUOA.js.map +1 -0
- package/dist/{chunk-DES5CSPH.js → chunk-4URRFBUS.js} +10 -10
- package/dist/chunk-4URRFBUS.js.map +1 -0
- package/dist/{chunk-DXT6TWW4.js → chunk-CLSGXTOL.js} +232 -435
- package/dist/chunk-CLSGXTOL.js.map +1 -0
- package/dist/{chunk-W66Z3C5H.js → chunk-FJYRWU2V.js} +5 -5
- package/dist/chunk-FJYRWU2V.js.map +1 -0
- package/dist/{chunk-VXKJ5ZIV.js → chunk-KQ6BLN6W.js} +172 -155
- package/dist/chunk-KQ6BLN6W.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/{diff-4YFDNEZB.js → diff-2T7UDES7.js} +12 -12
- package/dist/diff-2T7UDES7.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/{reset-QVERBAQJ.js → reset-QRXG7KZZ.js} +8 -8
- package/dist/reset-QRXG7KZZ.js.map +1 -0
- package/dist/{setup-ZSMZ7HZG.js → setup-QUUJ7SH3.js} +8 -8
- package/dist/setup-QUUJ7SH3.js.map +1 -0
- package/dist/sync-ISBJ7X2T.js +9 -0
- package/dist/{upgrade-WILVVHUY.js → upgrade-FALAUUKE.js} +22 -10
- package/dist/upgrade-FALAUUKE.js.map +1 -0
- package/package.json +15 -14
- package/templates/SAFEWORD.md +4 -2
- package/templates/commands/cleanup-zombies.md +48 -0
- package/templates/guides/zombie-process-cleanup.md +40 -24
- package/templates/scripts/cleanup-zombies.sh +222 -0
- package/templates/scripts/lint-md.sh +0 -0
- package/dist/check-I2J6THGQ.js.map +0 -1
- package/dist/chunk-DES5CSPH.js.map +0 -1
- package/dist/chunk-DXT6TWW4.js.map +0 -1
- package/dist/chunk-VXKJ5ZIV.js.map +0 -1
- package/dist/chunk-W66Z3C5H.js.map +0 -1
- package/dist/diff-4YFDNEZB.js.map +0 -1
- package/dist/reset-QVERBAQJ.js.map +0 -1
- package/dist/setup-ZSMZ7HZG.js.map +0 -1
- package/dist/sync-VQW5DSTV.js +0 -9
- package/dist/upgrade-WILVVHUY.js.map +0 -1
- /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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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;
|
|
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-
|
|
9
|
+
} from "./chunk-KQ6BLN6W.js";
|
|
10
10
|
import {
|
|
11
11
|
SAFEWORD_SCHEMA,
|
|
12
12
|
exists,
|
|
13
13
|
readFileSafe
|
|
14
|
-
} from "./chunk-
|
|
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
|
|
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 ===
|
|
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()
|
|
104
|
+
} else if (currentContent.trim() === action.content.trim()) {
|
|
105
105
|
diffs.push({
|
|
106
106
|
path: action.path,
|
|
107
|
-
status: "
|
|
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: "
|
|
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
|
|
126
|
-
if (!exists(
|
|
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(
|
|
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-
|
|
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
|
@@ -7,20 +7,20 @@ import {
|
|
|
7
7
|
reconcile,
|
|
8
8
|
success,
|
|
9
9
|
warn
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KQ6BLN6W.js";
|
|
11
11
|
import {
|
|
12
12
|
SAFEWORD_SCHEMA,
|
|
13
13
|
exists
|
|
14
|
-
} from "./chunk-
|
|
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
|
|
23
|
-
if (!exists(
|
|
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 (
|
|
67
|
-
error(`Reset failed: ${
|
|
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-
|
|
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-
|
|
11
|
+
} from "./chunk-KQ6BLN6W.js";
|
|
12
12
|
import {
|
|
13
13
|
SAFEWORD_SCHEMA,
|
|
14
14
|
exists,
|
|
15
15
|
writeJson
|
|
16
|
-
} from "./chunk-
|
|
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
|
|
38
|
-
if (exists(
|
|
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-
|
|
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":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compareVersions
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FJYRWU2V.js";
|
|
4
4
|
import {
|
|
5
5
|
sync
|
|
6
|
-
} from "./chunk-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
30
|
-
if (!exists(
|
|
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(
|
|
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-
|
|
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.
|
|
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
|
-
|
|
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
|
+
}
|
package/templates/SAFEWORD.md
CHANGED
|
@@ -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. **
|
|
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
|
-
##
|
|
73
|
+
## Built-in Cleanup Script
|
|
56
74
|
|
|
57
|
-
|
|
75
|
+
Safeword includes a cleanup script at `.safeword/scripts/cleanup-zombies.sh`:
|
|
58
76
|
|
|
59
77
|
```bash
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
81
|
+
# Preview first (recommended)
|
|
82
|
+
./.safeword/scripts/cleanup-zombies.sh --dry-run
|
|
68
83
|
|
|
69
|
-
#
|
|
70
|
-
|
|
84
|
+
# Explicit port override
|
|
85
|
+
./.safeword/scripts/cleanup-zombies.sh 5173
|
|
71
86
|
|
|
72
|
-
#
|
|
73
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
|
228
|
-
-
|
|
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
|