@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 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 = stableStringify(typedEntry.matcher || {});
168
+ const matcherKey = String(typedEntry.matcher ?? "");
175
169
  const existing = hooks[event].find(
176
- (h) => stableStringify(h.matcher || {}) === matcherKey
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 && "matcher" in entry && typeof entry.matcher === "object" && "hooks" in entry && Array.isArray(entry.hooks)) {
298
- return entry;
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
- await ensureDir(join2(cwd, "docs"));
335
+ const artifactsDir = join2(sniperDir, "artifacts");
336
+ await ensureDir(artifactsDir);
334
337
  const registryTemplate = join2(corePath, "templates", "registry.md");
335
- const registryDest = join2(cwd, "docs", "registry.md");
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 docs/registry.md" : "Created docs/ with registry.md");
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 as resolve2, sep as sep2 } from "path";
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 = resolve2(base, untrusted);
843
- const safeBase = resolve2(base) + sep2;
844
- if (!full.startsWith(safeBase) && full !== resolve2(base)) {
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 resolve3, dirname as dirname2 } from "path";
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 = resolve3(cwd);
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)) {