safeword 0.6.2 → 0.6.4
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-PECCGHEA.js → check-ICZISZ3R.js} +41 -23
- package/dist/check-ICZISZ3R.js.map +1 -0
- package/dist/chunk-E5ZC6R5H.js +720 -0
- package/dist/chunk-E5ZC6R5H.js.map +1 -0
- package/dist/chunk-JGXYBPNM.js +454 -0
- package/dist/chunk-JGXYBPNM.js.map +1 -0
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/diff-FOJDBKKF.js +168 -0
- package/dist/diff-FOJDBKKF.js.map +1 -0
- package/dist/reset-JU2E65XN.js +74 -0
- package/dist/reset-JU2E65XN.js.map +1 -0
- package/dist/setup-UKMYK5TE.js +103 -0
- package/dist/setup-UKMYK5TE.js.map +1 -0
- package/dist/{sync-4XBMKLXS.js → sync-5MOXVTH4.js} +33 -32
- package/dist/sync-5MOXVTH4.js.map +1 -0
- package/dist/upgrade-NSLDFWNR.js +73 -0
- package/dist/upgrade-NSLDFWNR.js.map +1 -0
- package/package.json +14 -15
- package/templates/SAFEWORD.md +8 -28
- package/templates/hooks/stop-quality.sh +21 -9
- package/dist/check-PECCGHEA.js.map +0 -1
- package/dist/chunk-6CVTH67L.js +0 -43
- package/dist/chunk-6CVTH67L.js.map +0 -1
- package/dist/chunk-75FKNZUM.js +0 -15
- package/dist/chunk-75FKNZUM.js.map +0 -1
- package/dist/chunk-ARIAOK2F.js +0 -110
- package/dist/chunk-ARIAOK2F.js.map +0 -1
- package/dist/chunk-FRPJITGG.js +0 -35
- package/dist/chunk-FRPJITGG.js.map +0 -1
- package/dist/chunk-IWWBZVHT.js +0 -274
- package/dist/chunk-IWWBZVHT.js.map +0 -1
- package/dist/diff-ZACVJKOU.js +0 -171
- package/dist/diff-ZACVJKOU.js.map +0 -1
- package/dist/reset-5SRM3P6J.js +0 -145
- package/dist/reset-5SRM3P6J.js.map +0 -1
- package/dist/setup-65EVU5OT.js +0 -437
- package/dist/setup-65EVU5OT.js.map +0 -1
- package/dist/sync-4XBMKLXS.js.map +0 -1
- package/dist/upgrade-P3WX3ODU.js +0 -153
- package/dist/upgrade-P3WX3ODU.js.map +0 -1
- /package/templates/prompts/{review.md → quality-review.md} +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-UKMYK5TE.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-ICZISZ3R.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-NSLDFWNR.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-FOJDBKKF.js");
|
|
24
24
|
await diff(options);
|
|
25
25
|
});
|
|
26
|
-
program.command("reset").description("Remove safeword configuration from project").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
27
|
-
const { reset } = await import("./reset-
|
|
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-JU2E65XN.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-5MOXVTH4.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 .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,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';\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":[]}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProjectContext,
|
|
3
|
+
error,
|
|
4
|
+
header,
|
|
5
|
+
info,
|
|
6
|
+
listItem,
|
|
7
|
+
reconcile,
|
|
8
|
+
success
|
|
9
|
+
} from "./chunk-JGXYBPNM.js";
|
|
10
|
+
import {
|
|
11
|
+
SAFEWORD_SCHEMA,
|
|
12
|
+
exists,
|
|
13
|
+
readFileSafe
|
|
14
|
+
} from "./chunk-E5ZC6R5H.js";
|
|
15
|
+
import {
|
|
16
|
+
VERSION
|
|
17
|
+
} from "./chunk-ORQHKDT2.js";
|
|
18
|
+
|
|
19
|
+
// src/commands/diff.ts
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
function createUnifiedDiff(oldContent, newContent, filename) {
|
|
22
|
+
const oldLines = oldContent.split("\n");
|
|
23
|
+
const newLines = newContent.split("\n");
|
|
24
|
+
const lines = [];
|
|
25
|
+
lines.push(`--- a/${filename}`);
|
|
26
|
+
lines.push(`+++ b/${filename}`);
|
|
27
|
+
let hasChanges = false;
|
|
28
|
+
const maxLines = Math.max(oldLines.length, newLines.length);
|
|
29
|
+
for (let i = 0; i < maxLines; i++) {
|
|
30
|
+
const oldLine = oldLines[i];
|
|
31
|
+
const newLine = newLines[i];
|
|
32
|
+
if (oldLine === newLine) {
|
|
33
|
+
lines.push(` ${oldLine ?? ""}`);
|
|
34
|
+
} else {
|
|
35
|
+
hasChanges = true;
|
|
36
|
+
if (oldLine !== void 0) {
|
|
37
|
+
lines.push(`-${oldLine}`);
|
|
38
|
+
}
|
|
39
|
+
if (newLine !== void 0) {
|
|
40
|
+
lines.push(`+${newLine}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!hasChanges) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);
|
|
48
|
+
return lines.join("\n");
|
|
49
|
+
}
|
|
50
|
+
function actionsToDiffs(actions, cwd) {
|
|
51
|
+
const diffs = [];
|
|
52
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
53
|
+
for (const action of actions) {
|
|
54
|
+
if (action.type === "write") {
|
|
55
|
+
if (seenPaths.has(action.path)) continue;
|
|
56
|
+
seenPaths.add(action.path);
|
|
57
|
+
const fullPath = join(cwd, action.path);
|
|
58
|
+
const currentContent = readFileSafe(fullPath);
|
|
59
|
+
if (currentContent === null) {
|
|
60
|
+
diffs.push({
|
|
61
|
+
path: action.path,
|
|
62
|
+
status: "added",
|
|
63
|
+
newContent: action.content
|
|
64
|
+
});
|
|
65
|
+
} else if (currentContent.trim() !== action.content.trim()) {
|
|
66
|
+
diffs.push({
|
|
67
|
+
path: action.path,
|
|
68
|
+
status: "modified",
|
|
69
|
+
currentContent,
|
|
70
|
+
newContent: action.content
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
diffs.push({
|
|
74
|
+
path: action.path,
|
|
75
|
+
status: "unchanged",
|
|
76
|
+
currentContent,
|
|
77
|
+
newContent: action.content
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return diffs;
|
|
83
|
+
}
|
|
84
|
+
async function diff(options) {
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const safewordDir = join(cwd, ".safeword");
|
|
87
|
+
if (!exists(safewordDir)) {
|
|
88
|
+
error("Not configured. Run `safeword setup` first.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
const versionPath = join(safewordDir, "version");
|
|
92
|
+
const projectVersion = readFileSafe(versionPath)?.trim() ?? "unknown";
|
|
93
|
+
header("Safeword Diff");
|
|
94
|
+
info(`Changes from v${projectVersion} \u2192 v${VERSION}`);
|
|
95
|
+
const ctx = createProjectContext(cwd);
|
|
96
|
+
const result = await reconcile(SAFEWORD_SCHEMA, "upgrade", ctx, { dryRun: true });
|
|
97
|
+
const diffs = actionsToDiffs(result.actions, cwd);
|
|
98
|
+
const added = diffs.filter((d) => d.status === "added");
|
|
99
|
+
const modified = diffs.filter((d) => d.status === "modified");
|
|
100
|
+
const unchanged = diffs.filter((d) => d.status === "unchanged");
|
|
101
|
+
info(
|
|
102
|
+
`
|
|
103
|
+
Summary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`
|
|
104
|
+
);
|
|
105
|
+
if (result.packagesToInstall.length > 0) {
|
|
106
|
+
info(`
|
|
107
|
+
Packages to install: ${result.packagesToInstall.length}`);
|
|
108
|
+
}
|
|
109
|
+
if (added.length > 0) {
|
|
110
|
+
info("\nAdded:");
|
|
111
|
+
for (const file of added) {
|
|
112
|
+
listItem(file.path);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (modified.length > 0) {
|
|
116
|
+
info("\nModified:");
|
|
117
|
+
for (const file of modified) {
|
|
118
|
+
listItem(file.path);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (unchanged.length > 0) {
|
|
122
|
+
info("\nUnchanged:");
|
|
123
|
+
for (const file of unchanged) {
|
|
124
|
+
listItem(file.path);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (options.verbose) {
|
|
128
|
+
header("Detailed Changes");
|
|
129
|
+
for (const file of modified) {
|
|
130
|
+
if (file.currentContent && file.newContent) {
|
|
131
|
+
info(`
|
|
132
|
+
${file.path}:`);
|
|
133
|
+
const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);
|
|
134
|
+
if (diffOutput) {
|
|
135
|
+
console.log(diffOutput);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const file of added) {
|
|
140
|
+
if (file.newContent) {
|
|
141
|
+
info(`
|
|
142
|
+
${file.path}: (new file)`);
|
|
143
|
+
const lines = file.newContent.split("\n").slice(0, 10);
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
console.log(`+${line}`);
|
|
146
|
+
}
|
|
147
|
+
if (file.newContent.split("\n").length > 10) {
|
|
148
|
+
console.log("... (truncated)");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (result.packagesToInstall.length > 0) {
|
|
153
|
+
info("\nPackages to install:");
|
|
154
|
+
for (const pkg of result.packagesToInstall) {
|
|
155
|
+
listItem(pkg);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (added.length === 0 && modified.length === 0 && result.packagesToInstall.length === 0) {
|
|
160
|
+
success("\nNo changes needed - configuration is up to date");
|
|
161
|
+
} else {
|
|
162
|
+
info("\nRun `safeword upgrade` to apply these changes");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
diff
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=diff-FOJDBKKF.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 { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFileSafe } from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { reconcile, type Action } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.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 */\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[] = [];\n lines.push(`--- a/${filename}`);\n lines.push(`+++ 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 = oldLines[i];\n const newLine = 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 * Convert reconcile actions to file diffs\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 = join(cwd, action.path);\n const currentContent = readFileSafe(fullPath);\n\n if (currentContent === null) {\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: 'modified',\n currentContent,\n newContent: action.content,\n });\n } else {\n diffs.push({\n path: action.path,\n status: 'unchanged',\n currentContent,\n newContent: action.content,\n });\n }\n }\n }\n\n return diffs;\n}\n\nexport async function diff(options: DiffOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, '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 if (added.length > 0) {\n info('\\nAdded:');\n for (const file of added) {\n listItem(file.path);\n }\n }\n\n if (modified.length > 0) {\n info('\\nModified:');\n for (const file of modified) {\n listItem(file.path);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file.path);\n }\n }\n\n // Verbose output - show actual diffs\n if (options.verbose) {\n header('Detailed Changes');\n\n for (const file of modified) {\n if (file.currentContent && file.newContent) {\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 for (const file of added) {\n if (file.newContent) {\n info(`\\n${file.path}: (new file)`);\n const lines = file.newContent.split('\\n').slice(0, 10);\n for (const line of lines) {\n console.log(`+${line}`);\n }\n if (file.newContent.split('\\n').length > 10) {\n console.log('... (truncated)');\n }\n }\n }\n\n if (result.packagesToInstall.length > 0) {\n info('\\nPackages to install:');\n for (const pkg of result.packagesToInstall) {\n listItem(pkg);\n }\n }\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":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProjectContext,
|
|
3
|
+
error,
|
|
4
|
+
header,
|
|
5
|
+
info,
|
|
6
|
+
listItem,
|
|
7
|
+
reconcile,
|
|
8
|
+
success,
|
|
9
|
+
warn
|
|
10
|
+
} from "./chunk-JGXYBPNM.js";
|
|
11
|
+
import {
|
|
12
|
+
SAFEWORD_SCHEMA,
|
|
13
|
+
exists
|
|
14
|
+
} from "./chunk-E5ZC6R5H.js";
|
|
15
|
+
import "./chunk-ORQHKDT2.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/reset.ts
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { execSync } from "child_process";
|
|
20
|
+
async function reset(options) {
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
const safewordDir = join(cwd, ".safeword");
|
|
23
|
+
if (!exists(safewordDir)) {
|
|
24
|
+
info("Nothing to remove. Project is not configured with safeword.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const fullReset = options.full ?? false;
|
|
28
|
+
header("Safeword Reset");
|
|
29
|
+
if (fullReset) {
|
|
30
|
+
info("Performing full reset (including linting configuration)...");
|
|
31
|
+
} else {
|
|
32
|
+
info("Removing safeword configuration...");
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const mode = fullReset ? "uninstall-full" : "uninstall";
|
|
36
|
+
const ctx = createProjectContext(cwd);
|
|
37
|
+
const result = await reconcile(SAFEWORD_SCHEMA, mode, ctx);
|
|
38
|
+
if (fullReset && result.packagesToRemove.length > 0) {
|
|
39
|
+
info("\nUninstalling devDependencies...");
|
|
40
|
+
try {
|
|
41
|
+
const uninstallCmd = `npm uninstall ${result.packagesToRemove.join(" ")}`;
|
|
42
|
+
info(`Running: ${uninstallCmd}`);
|
|
43
|
+
execSync(uninstallCmd, { cwd, stdio: "inherit" });
|
|
44
|
+
success("Uninstalled safeword devDependencies");
|
|
45
|
+
} catch {
|
|
46
|
+
warn("Failed to uninstall some packages. Run manually:");
|
|
47
|
+
listItem(`npm uninstall ${result.packagesToRemove.join(" ")}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
header("Reset Complete");
|
|
51
|
+
if (result.removed.length > 0) {
|
|
52
|
+
info("\nRemoved:");
|
|
53
|
+
for (const item of result.removed) {
|
|
54
|
+
listItem(item);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!fullReset) {
|
|
58
|
+
info("\nPreserved (use --full to remove):");
|
|
59
|
+
listItem("eslint.config.mjs");
|
|
60
|
+
listItem(".prettierrc");
|
|
61
|
+
listItem(".markdownlint-cli2.jsonc");
|
|
62
|
+
listItem("package.json (scripts, lint-staged config)");
|
|
63
|
+
listItem("devDependencies (eslint, prettier, husky, lint-staged, etc.)");
|
|
64
|
+
}
|
|
65
|
+
success("\nSafeword configuration removed");
|
|
66
|
+
} catch (err) {
|
|
67
|
+
error(`Reset failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
reset
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=reset-JU2E65XN.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 { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { exists } from '../utils/fs.js';\nimport { info, success, warn, error, header, listItem } from '../utils/output.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\n\nexport interface ResetOptions {\n yes?: boolean;\n full?: boolean;\n}\n\nexport async function reset(options: ResetOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\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 (err) {\n error(`Reset failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AASA,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAYzB,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AAGzC,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,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,KAAK;AACZ,UAAM,iBAAiB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProjectContext,
|
|
3
|
+
error,
|
|
4
|
+
header,
|
|
5
|
+
info,
|
|
6
|
+
isGitRepo,
|
|
7
|
+
listItem,
|
|
8
|
+
reconcile,
|
|
9
|
+
success,
|
|
10
|
+
warn
|
|
11
|
+
} from "./chunk-JGXYBPNM.js";
|
|
12
|
+
import {
|
|
13
|
+
SAFEWORD_SCHEMA,
|
|
14
|
+
exists,
|
|
15
|
+
writeJson
|
|
16
|
+
} from "./chunk-E5ZC6R5H.js";
|
|
17
|
+
import {
|
|
18
|
+
VERSION
|
|
19
|
+
} from "./chunk-ORQHKDT2.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/setup.ts
|
|
22
|
+
import { join, basename } from "path";
|
|
23
|
+
import { execSync } from "child_process";
|
|
24
|
+
async function setup(options) {
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
const safewordDir = join(cwd, ".safeword");
|
|
27
|
+
if (exists(safewordDir)) {
|
|
28
|
+
error("Already configured. Run `safeword upgrade` to update.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
32
|
+
let packageJsonCreated = false;
|
|
33
|
+
if (!exists(packageJsonPath)) {
|
|
34
|
+
const dirName = basename(cwd) || "project";
|
|
35
|
+
const defaultPackageJson = {
|
|
36
|
+
name: dirName,
|
|
37
|
+
version: "0.1.0",
|
|
38
|
+
scripts: {}
|
|
39
|
+
};
|
|
40
|
+
writeJson(packageJsonPath, defaultPackageJson);
|
|
41
|
+
packageJsonCreated = true;
|
|
42
|
+
}
|
|
43
|
+
const isNonInteractive = options.yes || !process.stdin.isTTY;
|
|
44
|
+
header("Safeword Setup");
|
|
45
|
+
info(`Version: ${VERSION}`);
|
|
46
|
+
if (packageJsonCreated) {
|
|
47
|
+
info("Created package.json (none found)");
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
info("\nCreating safeword configuration...");
|
|
51
|
+
const ctx = createProjectContext(cwd);
|
|
52
|
+
const result = await reconcile(SAFEWORD_SCHEMA, "install", ctx);
|
|
53
|
+
success("Created .safeword directory and configuration");
|
|
54
|
+
if (result.packagesToInstall.length > 0) {
|
|
55
|
+
info("\nInstalling linting dependencies...");
|
|
56
|
+
try {
|
|
57
|
+
const installCmd = `npm install -D ${result.packagesToInstall.join(" ")}`;
|
|
58
|
+
info(`Running: ${installCmd}`);
|
|
59
|
+
execSync(installCmd, { cwd, stdio: "inherit" });
|
|
60
|
+
success("Installed linting dependencies");
|
|
61
|
+
} catch {
|
|
62
|
+
warn("Failed to install dependencies. Run manually:");
|
|
63
|
+
listItem(`npm install -D ${result.packagesToInstall.join(" ")}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!isGitRepo(cwd)) {
|
|
67
|
+
if (isNonInteractive) {
|
|
68
|
+
warn("Skipped Husky setup (no git repository)");
|
|
69
|
+
} else {
|
|
70
|
+
warn("Skipped Husky setup (no .git directory)");
|
|
71
|
+
info("Initialize git and run safeword upgrade to enable pre-commit hooks");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
header("Setup Complete");
|
|
75
|
+
if (result.created.length > 0 || packageJsonCreated) {
|
|
76
|
+
info("\nCreated:");
|
|
77
|
+
if (packageJsonCreated) {
|
|
78
|
+
listItem("package.json");
|
|
79
|
+
}
|
|
80
|
+
for (const file of result.created) {
|
|
81
|
+
listItem(file);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (result.updated.length > 0) {
|
|
85
|
+
info("\nModified:");
|
|
86
|
+
for (const file of result.updated) {
|
|
87
|
+
listItem(file);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
info("\nNext steps:");
|
|
91
|
+
listItem("Run `safeword check` to verify setup");
|
|
92
|
+
listItem("Commit the new files to git");
|
|
93
|
+
success(`
|
|
94
|
+
Safeword ${VERSION} installed successfully!`);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
error(`Setup failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
setup
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=setup-UKMYK5TE.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 { join, basename } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, writeJson } from '../utils/fs.js';\nimport { info, success, warn, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { execSync } from 'node:child_process';\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\nexport async function setup(options: SetupOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if already configured\n if (exists(safewordDir)) {\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 = join(cwd, 'package.json');\n let packageJsonCreated = false;\n if (!exists(packageJsonPath)) {\n const dirName = 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 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\n if (result.created.length > 0 || packageJsonCreated) {\n info('\\nCreated:');\n if (packageJsonCreated) {\n listItem('package.json');\n }\n for (const file of result.created) {\n listItem(file);\n }\n }\n\n if (result.updated.length > 0) {\n info('\\nModified:');\n for (const file of result.updated) {\n listItem(file);\n }\n }\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 (err) {\n error(`Setup failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAMA,SAAS,MAAM,gBAAgB;AAQ/B,SAAS,gBAAgB;AAezB,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AAGzC,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,uDAAuD;AAC7D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,kBAAkB,KAAK,KAAK,cAAc;AAChD,MAAI,qBAAqB;AACzB,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,UAAM,UAAU,SAAS,GAAG,KAAK;AACjC,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;AAC7B,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;AAEvB,QAAI,OAAO,QAAQ,SAAS,KAAK,oBAAoB;AACnD,WAAK,YAAY;AACjB,UAAI,oBAAoB;AACtB,iBAAS,cAAc;AAAA,MACzB;AACA,iBAAW,QAAQ,OAAO,SAAS;AACjC,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,WAAK,aAAa;AAClB,iBAAW,QAAQ,OAAO,SAAS;AACjC,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAEA,SAAK,eAAe;AACpB,aAAS,sCAAsC;AAC/C,aAAS,6BAA6B;AAEtC,YAAQ;AAAA,WAAc,OAAO,0BAA0B;AAAA,EACzD,SAAS,KAAK;AACZ,UAAM,iBAAiB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -1,49 +1,50 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
2
|
+
SAFEWORD_SCHEMA,
|
|
3
|
+
detectProjectType,
|
|
5
4
|
exists,
|
|
6
5
|
readJson
|
|
7
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-E5ZC6R5H.js";
|
|
7
|
+
import "./chunk-ORQHKDT2.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/sync.ts
|
|
10
10
|
import { join } from "path";
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
|
+
var BASE_ESLINT_PACKAGES = [
|
|
13
|
+
"eslint",
|
|
14
|
+
"@eslint/js",
|
|
15
|
+
"eslint-plugin-import-x",
|
|
16
|
+
"eslint-plugin-sonarjs",
|
|
17
|
+
"@microsoft/eslint-plugin-sdl",
|
|
18
|
+
"eslint-config-prettier",
|
|
19
|
+
"eslint-plugin-boundaries",
|
|
20
|
+
"eslint-plugin-playwright"
|
|
21
|
+
];
|
|
12
22
|
function getRequiredPlugins(projectType) {
|
|
13
|
-
const plugins = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"eslint-plugin-import-x",
|
|
18
|
-
"eslint-plugin-sonarjs",
|
|
19
|
-
"@microsoft/eslint-plugin-sdl",
|
|
20
|
-
"eslint-config-prettier",
|
|
21
|
-
"eslint-plugin-boundaries",
|
|
22
|
-
"eslint-plugin-playwright"
|
|
23
|
-
];
|
|
24
|
-
if (projectType.typescript) {
|
|
25
|
-
plugins.push("typescript-eslint");
|
|
23
|
+
const plugins = [...BASE_ESLINT_PACKAGES];
|
|
24
|
+
const { conditional } = SAFEWORD_SCHEMA.packages;
|
|
25
|
+
if (projectType.typescript && conditional.typescript) {
|
|
26
|
+
plugins.push(...conditional.typescript);
|
|
26
27
|
}
|
|
27
|
-
if (projectType.react || projectType.nextjs) {
|
|
28
|
-
plugins.push(
|
|
28
|
+
if ((projectType.react || projectType.nextjs) && conditional.react) {
|
|
29
|
+
plugins.push(...conditional.react);
|
|
29
30
|
}
|
|
30
|
-
if (projectType.nextjs) {
|
|
31
|
-
plugins.push(
|
|
31
|
+
if (projectType.nextjs && conditional.nextjs) {
|
|
32
|
+
plugins.push(...conditional.nextjs);
|
|
32
33
|
}
|
|
33
|
-
if (projectType.astro) {
|
|
34
|
-
plugins.push(
|
|
34
|
+
if (projectType.astro && conditional.astro) {
|
|
35
|
+
plugins.push(...conditional.astro);
|
|
35
36
|
}
|
|
36
|
-
if (projectType.vue) {
|
|
37
|
-
plugins.push(
|
|
37
|
+
if (projectType.vue && conditional.vue) {
|
|
38
|
+
plugins.push(...conditional.vue);
|
|
38
39
|
}
|
|
39
|
-
if (projectType.svelte) {
|
|
40
|
-
plugins.push(
|
|
40
|
+
if (projectType.svelte && conditional.svelte) {
|
|
41
|
+
plugins.push(...conditional.svelte);
|
|
41
42
|
}
|
|
42
|
-
if (projectType.electron) {
|
|
43
|
-
plugins.push(
|
|
43
|
+
if (projectType.electron && conditional.electron) {
|
|
44
|
+
plugins.push(...conditional.electron);
|
|
44
45
|
}
|
|
45
|
-
if (projectType.vitest) {
|
|
46
|
-
plugins.push(
|
|
46
|
+
if (projectType.vitest && conditional.vitest) {
|
|
47
|
+
plugins.push(...conditional.vitest);
|
|
47
48
|
}
|
|
48
49
|
return plugins;
|
|
49
50
|
}
|
|
@@ -113,4 +114,4 @@ Run manually when online:`);
|
|
|
113
114
|
export {
|
|
114
115
|
sync
|
|
115
116
|
};
|
|
116
|
-
//# sourceMappingURL=sync-
|
|
117
|
+
//# sourceMappingURL=sync-5MOXVTH4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/sync.ts"],"sourcesContent":["/**\n * Sync command - Keep linting plugins in sync with project dependencies\n *\n * Detects frameworks in package.json and ensures the corresponding ESLint plugins\n * are installed. Designed to be called from Husky pre-commit hook.\n *\n * Behavior:\n * - Fast exit when nothing needs to change\n * - Installs missing plugins\n * - Optionally stages modified files (--stage flag for pre-commit)\n * - Clear error message if installation fails\n */\n\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { exists, readJson } from '../utils/fs.js';\nimport {\n detectProjectType,\n type PackageJson,\n type ProjectType,\n} from '../utils/project-detector.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\n\nexport interface SyncOptions {\n quiet?: boolean;\n stage?: boolean;\n}\n\n/**\n * Base ESLint packages always required for linting.\n * Explicit list for clarity - sync only cares about ESLint plugins.\n *\n * NOTE: This is a subset of SAFEWORD_SCHEMA.packages.base (which includes\n * prettier, markdownlint-cli2, husky, lint-staged, knip). When adding new\n * ESLint packages to schema.ts, also add them here if they should be\n * auto-installed on pre-commit.\n */\nconst BASE_ESLINT_PACKAGES = [\n 'eslint',\n '@eslint/js',\n 'eslint-plugin-import-x',\n 'eslint-plugin-sonarjs',\n '@microsoft/eslint-plugin-sdl',\n 'eslint-config-prettier',\n 'eslint-plugin-boundaries',\n 'eslint-plugin-playwright',\n];\n\n/**\n * Get required ESLint packages based on project type.\n * Uses explicit base list + SAFEWORD_SCHEMA.packages.conditional for frameworks.\n */\nfunction getRequiredPlugins(projectType: ProjectType): string[] {\n const plugins: string[] = [...BASE_ESLINT_PACKAGES];\n\n // Add conditional packages from schema based on detected project type\n const { conditional } = SAFEWORD_SCHEMA.packages;\n\n if (projectType.typescript && conditional.typescript) {\n plugins.push(...conditional.typescript);\n }\n if ((projectType.react || projectType.nextjs) && conditional.react) {\n plugins.push(...conditional.react);\n }\n if (projectType.nextjs && conditional.nextjs) {\n plugins.push(...conditional.nextjs);\n }\n if (projectType.astro && conditional.astro) {\n plugins.push(...conditional.astro);\n }\n if (projectType.vue && conditional.vue) {\n plugins.push(...conditional.vue);\n }\n if (projectType.svelte && conditional.svelte) {\n plugins.push(...conditional.svelte);\n }\n if (projectType.electron && conditional.electron) {\n plugins.push(...conditional.electron);\n }\n if (projectType.vitest && conditional.vitest) {\n plugins.push(...conditional.vitest);\n }\n\n return plugins;\n}\n\n/**\n * Check which packages are missing from devDependencies\n */\nfunction getMissingPackages(required: string[], installed: Record<string, string>): string[] {\n return required.filter(pkg => !(pkg in installed));\n}\n\n/**\n * Sync linting configuration with current project dependencies\n */\nexport async function sync(options: SyncOptions = {}): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n const packageJsonPath = join(cwd, 'package.json');\n\n // Must be in a safeword project\n if (!exists(safewordDir)) {\n if (!options.quiet) {\n console.error('Not a safeword project. Run `safeword setup` first.');\n }\n process.exit(1);\n }\n\n if (!exists(packageJsonPath)) {\n if (!options.quiet) {\n console.error('No package.json found.');\n }\n process.exit(1);\n }\n\n const packageJson = readJson<PackageJson>(packageJsonPath);\n if (!packageJson) {\n process.exit(1);\n }\n\n // Detect current project type\n const projectType = detectProjectType(packageJson);\n const devDeps = packageJson.devDependencies || {};\n\n // Check for missing plugins\n const requiredPlugins = getRequiredPlugins(projectType);\n const missingPlugins = getMissingPackages(requiredPlugins, devDeps);\n\n // Fast exit if nothing to install\n if (missingPlugins.length === 0) {\n return;\n }\n\n // Install missing plugins\n if (!options.quiet) {\n console.log(`Installing missing ESLint plugins: ${missingPlugins.join(', ')}`);\n }\n\n try {\n execSync(`npm install -D ${missingPlugins.join(' ')}`, {\n cwd,\n stdio: options.quiet ? 'pipe' : 'inherit',\n });\n } catch (error) {\n // Clear error message for network/install failures\n const pluginList = missingPlugins.join(' ');\n console.error(`\\n✗ Failed to install ESLint plugins\\n`);\n console.error(`Your project needs: ${pluginList}`);\n console.error(`\\nRun manually when online:`);\n console.error(` npm install -D ${pluginList}\\n`);\n process.exit(1);\n }\n\n // Stage modified files if --stage flag is set (for pre-commit hook)\n if (options.stage) {\n try {\n execSync('git add package.json package-lock.json', {\n cwd,\n stdio: 'pipe',\n });\n } catch {\n // Not in a git repo or git add failed - ignore\n }\n }\n\n if (!options.quiet) {\n console.log(`✓ Installed ${missingPlugins.length} ESLint plugin(s)`);\n }\n}\n"],"mappings":";;;;;;;;;AAaA,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAuBzB,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMA,SAAS,mBAAmB,aAAoC;AAC9D,QAAM,UAAoB,CAAC,GAAG,oBAAoB;AAGlD,QAAM,EAAE,YAAY,IAAI,gBAAgB;AAExC,MAAI,YAAY,cAAc,YAAY,YAAY;AACpD,YAAQ,KAAK,GAAG,YAAY,UAAU;AAAA,EACxC;AACA,OAAK,YAAY,SAAS,YAAY,WAAW,YAAY,OAAO;AAClE,YAAQ,KAAK,GAAG,YAAY,KAAK;AAAA,EACnC;AACA,MAAI,YAAY,UAAU,YAAY,QAAQ;AAC5C,YAAQ,KAAK,GAAG,YAAY,MAAM;AAAA,EACpC;AACA,MAAI,YAAY,SAAS,YAAY,OAAO;AAC1C,YAAQ,KAAK,GAAG,YAAY,KAAK;AAAA,EACnC;AACA,MAAI,YAAY,OAAO,YAAY,KAAK;AACtC,YAAQ,KAAK,GAAG,YAAY,GAAG;AAAA,EACjC;AACA,MAAI,YAAY,UAAU,YAAY,QAAQ;AAC5C,YAAQ,KAAK,GAAG,YAAY,MAAM;AAAA,EACpC;AACA,MAAI,YAAY,YAAY,YAAY,UAAU;AAChD,YAAQ,KAAK,GAAG,YAAY,QAAQ;AAAA,EACtC;AACA,MAAI,YAAY,UAAU,YAAY,QAAQ;AAC5C,YAAQ,KAAK,GAAG,YAAY,MAAM;AAAA,EACpC;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,UAAoB,WAA6C;AAC3F,SAAO,SAAS,OAAO,SAAO,EAAE,OAAO,UAAU;AACnD;AAKA,eAAsB,KAAK,UAAuB,CAAC,GAAkB;AACnE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AACzC,QAAM,kBAAkB,KAAK,KAAK,cAAc;AAGhD,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ,MAAM,qDAAqD;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ,MAAM,wBAAwB;AAAA,IACxC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,SAAsB,eAAe;AACzD,MAAI,CAAC,aAAa;AAChB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,kBAAkB,WAAW;AACjD,QAAM,UAAU,YAAY,mBAAmB,CAAC;AAGhD,QAAM,kBAAkB,mBAAmB,WAAW;AACtD,QAAM,iBAAiB,mBAAmB,iBAAiB,OAAO;AAGlE,MAAI,eAAe,WAAW,GAAG;AAC/B;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,OAAO;AAClB,YAAQ,IAAI,sCAAsC,eAAe,KAAK,IAAI,CAAC,EAAE;AAAA,EAC/E;AAEA,MAAI;AACF,aAAS,kBAAkB,eAAe,KAAK,GAAG,CAAC,IAAI;AAAA,MACrD;AAAA,MACA,OAAO,QAAQ,QAAQ,SAAS;AAAA,IAClC,CAAC;AAAA,EACH,SAAS,OAAO;AAEd,UAAM,aAAa,eAAe,KAAK,GAAG;AAC1C,YAAQ,MAAM;AAAA;AAAA,CAAwC;AACtD,YAAQ,MAAM,uBAAuB,UAAU,EAAE;AACjD,YAAQ,MAAM;AAAA,0BAA6B;AAC3C,YAAQ,MAAM,oBAAoB,UAAU;AAAA,CAAI;AAChD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,QAAQ,OAAO;AACjB,QAAI;AACF,eAAS,0CAA0C;AAAA,QACjD;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,OAAO;AAClB,YAAQ,IAAI,oBAAe,eAAe,MAAM,mBAAmB;AAAA,EACrE;AACF;","names":[]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareVersions
|
|
3
|
+
} from "./chunk-W66Z3C5H.js";
|
|
4
|
+
import {
|
|
5
|
+
createProjectContext,
|
|
6
|
+
error,
|
|
7
|
+
header,
|
|
8
|
+
info,
|
|
9
|
+
listItem,
|
|
10
|
+
reconcile,
|
|
11
|
+
success
|
|
12
|
+
} from "./chunk-JGXYBPNM.js";
|
|
13
|
+
import {
|
|
14
|
+
SAFEWORD_SCHEMA,
|
|
15
|
+
exists,
|
|
16
|
+
readFileSafe
|
|
17
|
+
} from "./chunk-E5ZC6R5H.js";
|
|
18
|
+
import {
|
|
19
|
+
VERSION
|
|
20
|
+
} from "./chunk-ORQHKDT2.js";
|
|
21
|
+
|
|
22
|
+
// src/commands/upgrade.ts
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
async function upgrade() {
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
const safewordDir = join(cwd, ".safeword");
|
|
27
|
+
if (!exists(safewordDir)) {
|
|
28
|
+
error("Not configured. Run `safeword setup` first.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const versionPath = join(safewordDir, "version");
|
|
32
|
+
const projectVersion = readFileSafe(versionPath)?.trim() ?? "0.0.0";
|
|
33
|
+
if (compareVersions(VERSION, projectVersion) < 0) {
|
|
34
|
+
error(`CLI v${VERSION} is older than project v${projectVersion}.`);
|
|
35
|
+
error("Update the CLI first: npm install -g safeword");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
header("Safeword Upgrade");
|
|
39
|
+
info(`Upgrading from v${projectVersion} to v${VERSION}`);
|
|
40
|
+
try {
|
|
41
|
+
const ctx = createProjectContext(cwd);
|
|
42
|
+
const result = await reconcile(SAFEWORD_SCHEMA, "upgrade", ctx);
|
|
43
|
+
header("Upgrade Complete");
|
|
44
|
+
info(`
|
|
45
|
+
Version: v${projectVersion} \u2192 v${VERSION}`);
|
|
46
|
+
if (result.created.length > 0) {
|
|
47
|
+
info("\nCreated:");
|
|
48
|
+
for (const file of result.created) {
|
|
49
|
+
listItem(file);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (result.updated.length > 0) {
|
|
53
|
+
info("\nUpdated:");
|
|
54
|
+
for (const file of result.updated) {
|
|
55
|
+
listItem(file);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (result.packagesToInstall.length > 0) {
|
|
59
|
+
info(`
|
|
60
|
+
Packages to install: ${result.packagesToInstall.length}`);
|
|
61
|
+
info("Run `safeword sync` to install missing packages");
|
|
62
|
+
}
|
|
63
|
+
success(`
|
|
64
|
+
Safeword upgraded to v${VERSION}`);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
error(`Upgrade failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
upgrade
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=upgrade-NSLDFWNR.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 { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFileSafe } from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { compareVersions } from '../utils/version.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\n\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, '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 // Report packages that need installation\n if (result.packagesToInstall.length > 0) {\n info(`\\nPackages to install: ${result.packagesToInstall.length}`);\n info('Run `safeword sync` to install missing packages');\n }\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (err) {\n error(`Upgrade failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAMA,SAAS,YAAY;AASrB,eAAsB,UAAyB;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AAGzC,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,UAAM,6CAA6C;AACnD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,KAAK,aAAa,SAAS;AAC/C,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,uBAA0B,OAAO,kBAAkB,MAAM,EAAE;AAChE,WAAK,iDAAiD;AAAA,IACxD;AAEA,YAAQ;AAAA,wBAA2B,OAAO,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ,UAAM,mBAAmB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|