@sniper.ai/cli 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +23 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -152,12 +152,6 @@ async function composeMixin(basePath, mixinPaths) {
|
|
|
152
152
|
}
|
|
153
153
|
return content;
|
|
154
154
|
}
|
|
155
|
-
function stableStringify(obj) {
|
|
156
|
-
if (obj === null || obj === void 0 || typeof obj !== "object") return JSON.stringify(obj ?? null);
|
|
157
|
-
if (Array.isArray(obj)) return "[" + obj.map(stableStringify).join(",") + "]";
|
|
158
|
-
const sorted = Object.keys(obj).sort();
|
|
159
|
-
return "{" + sorted.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
160
|
-
}
|
|
161
155
|
function mergeHooks(base, ...sources) {
|
|
162
156
|
const result = { ...base };
|
|
163
157
|
if (!result.hooks || typeof result.hooks !== "object") {
|
|
@@ -171,9 +165,9 @@ function mergeHooks(base, ...sources) {
|
|
|
171
165
|
if (!hooks[event]) hooks[event] = [];
|
|
172
166
|
for (const entry of entries) {
|
|
173
167
|
const typedEntry = entry;
|
|
174
|
-
const matcherKey =
|
|
168
|
+
const matcherKey = String(typedEntry.matcher ?? "");
|
|
175
169
|
const existing = hooks[event].find(
|
|
176
|
-
(h) =>
|
|
170
|
+
(h) => String(h.matcher ?? "") === matcherKey
|
|
177
171
|
);
|
|
178
172
|
if (existing) {
|
|
179
173
|
const existingHooks = existing.hooks || [];
|
|
@@ -294,12 +288,20 @@ async function scaffoldProject(cwd, config, options = {}) {
|
|
|
294
288
|
for (const [event, entries] of Object.entries(pluginContent.hooks)) {
|
|
295
289
|
if (!Array.isArray(entries)) continue;
|
|
296
290
|
pluginHooksFormatted[event] = entries.map((entry) => {
|
|
297
|
-
if (typeof entry === "object" && entry !== null && "
|
|
298
|
-
|
|
291
|
+
if (typeof entry === "object" && entry !== null && "hooks" in entry && Array.isArray(entry.hooks)) {
|
|
292
|
+
const e = entry;
|
|
293
|
+
if (typeof e.matcher === "object" && e.matcher !== null) {
|
|
294
|
+
const tools = e.matcher.tools;
|
|
295
|
+
if (Array.isArray(tools)) {
|
|
296
|
+
e.matcher = tools.join("|");
|
|
297
|
+
} else {
|
|
298
|
+
delete e.matcher;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return e;
|
|
299
302
|
}
|
|
300
303
|
const cmd = String(entry);
|
|
301
304
|
return {
|
|
302
|
-
matcher: {},
|
|
303
305
|
hooks: [{
|
|
304
306
|
type: "command",
|
|
305
307
|
description: `${pluginName} plugin: ${cmd.split(" ")[0]}`,
|
|
@@ -330,14 +332,13 @@ async function scaffoldProject(cwd, config, options = {}) {
|
|
|
330
332
|
} else {
|
|
331
333
|
log14.push("Skipped CLAUDE.md (preserved user customizations)");
|
|
332
334
|
}
|
|
333
|
-
|
|
335
|
+
const artifactsDir = join2(sniperDir, "artifacts");
|
|
336
|
+
await ensureDir(artifactsDir);
|
|
334
337
|
const registryTemplate = join2(corePath, "templates", "registry.md");
|
|
335
|
-
const registryDest = join2(
|
|
338
|
+
const registryDest = join2(artifactsDir, "registry.md");
|
|
336
339
|
if (await fileExists(registryTemplate) && !await fileExists(registryDest)) {
|
|
337
340
|
await cp(registryTemplate, registryDest);
|
|
338
|
-
log14.push(isUpdate ? "Created missing
|
|
339
|
-
} else if (!isUpdate) {
|
|
340
|
-
log14.push("Created docs/");
|
|
341
|
+
log14.push(isUpdate ? "Created missing .sniper/artifacts/registry.md" : "Created .sniper/artifacts/registry.md");
|
|
341
342
|
}
|
|
342
343
|
return log14;
|
|
343
344
|
}
|
|
@@ -832,16 +833,16 @@ import {
|
|
|
832
833
|
access as access4,
|
|
833
834
|
mkdir as mkdir3
|
|
834
835
|
} from "fs/promises";
|
|
835
|
-
import { join as join6, resolve
|
|
836
|
+
import { join as join6, resolve, sep } from "path";
|
|
836
837
|
import { execFileSync } from "child_process";
|
|
837
838
|
import YAML4 from "yaml";
|
|
838
839
|
function getPackageManagerCommand(config) {
|
|
839
840
|
return config?.stack?.package_manager || "pnpm";
|
|
840
841
|
}
|
|
841
842
|
function assertSafePath(base, untrusted) {
|
|
842
|
-
const full =
|
|
843
|
-
const safeBase =
|
|
844
|
-
if (!full.startsWith(safeBase) && full !==
|
|
843
|
+
const full = resolve(base, untrusted);
|
|
844
|
+
const safeBase = resolve(base) + sep;
|
|
845
|
+
if (!full.startsWith(safeBase) && full !== resolve(base)) {
|
|
845
846
|
throw new Error(
|
|
846
847
|
`Invalid name: path traversal detected in "${untrusted}"`
|
|
847
848
|
);
|
|
@@ -1652,7 +1653,7 @@ import * as p7 from "@clack/prompts";
|
|
|
1652
1653
|
|
|
1653
1654
|
// src/workspace-manager.ts
|
|
1654
1655
|
import { readFile as readFile8, writeFile as writeFile5, access as access6, mkdir as mkdir5 } from "fs/promises";
|
|
1655
|
-
import { join as join9, resolve as
|
|
1656
|
+
import { join as join9, resolve as resolve2, dirname as dirname2 } from "path";
|
|
1656
1657
|
import YAML7 from "yaml";
|
|
1657
1658
|
var WORKSPACE_DIR = ".sniper-workspace";
|
|
1658
1659
|
var WORKSPACE_CONFIG = "config.yaml";
|
|
@@ -1665,7 +1666,7 @@ async function pathExists3(p14) {
|
|
|
1665
1666
|
}
|
|
1666
1667
|
}
|
|
1667
1668
|
async function findWorkspaceRoot(cwd) {
|
|
1668
|
-
let dir =
|
|
1669
|
+
let dir = resolve2(cwd);
|
|
1669
1670
|
while (true) {
|
|
1670
1671
|
const configPath = join9(dir, WORKSPACE_DIR, WORKSPACE_CONFIG);
|
|
1671
1672
|
if (await pathExists3(configPath)) {
|