pi-supernova 0.0.4 → 0.0.6

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/runtime.js CHANGED
@@ -59,23 +59,32 @@ export async function runGuestProgram(options) {
59
59
 
60
60
  let compiled = compiledCache.get(body);
61
61
  if (!compiled) {
62
- compiled = new AsyncFunction(
63
- "nova",
64
- "tools",
65
- "console",
66
- "parallel",
67
- "pipeline",
68
- "read",
69
- "write",
70
- "edit",
71
- "patch",
72
- "surface",
73
- "snap",
74
- "bash",
75
- "exec",
76
- "speculate",
77
- body,
78
- );
62
+ try {
63
+ compiled = new AsyncFunction(
64
+ "nova",
65
+ "tools",
66
+ "console",
67
+ "parallel",
68
+ "pipeline",
69
+ "read",
70
+ "write",
71
+ "edit",
72
+ "patch",
73
+ "surface",
74
+ "snap",
75
+ "bash",
76
+ "exec",
77
+ "speculate",
78
+ body,
79
+ );
80
+ } catch (err) {
81
+ return {
82
+ ok: false,
83
+ error: err instanceof Error ? err.message : String(err),
84
+ logs,
85
+ wallMs: Math.round(performance.now() - started),
86
+ };
87
+ }
79
88
  if (compiledCache.size >= COMPILED_CACHE_MAX) {
80
89
  const first = compiledCache.keys().next().value;
81
90
  if (first !== undefined) compiledCache.delete(first);
@@ -108,6 +117,16 @@ export async function runGuestProgram(options) {
108
117
  return res;
109
118
  };
110
119
 
120
+ const unwrapJsonValue = (res) => {
121
+ const value = unwrapValue(res);
122
+ if (!isString(value)) return value;
123
+ try {
124
+ return JSON.parse(value);
125
+ } catch {
126
+ return value;
127
+ }
128
+ };
129
+
111
130
  const guestRead = async (p, off, lim) => {
112
131
  if (Array.isArray(p)) {
113
132
  return await Promise.all(p.map((item) => guestRead(item, off, lim)));
@@ -123,11 +142,11 @@ export async function runGuestProgram(options) {
123
142
  const guestPatch = async (p, d) => unwrapValue(await nova.call("apply_patch", { path: p, patch: d }));
124
143
  const guestSurface = async (p) => {
125
144
  const res = await (isFunction(nova.surface) ? nova.surface(p) : nova.call("surface", { path: p }));
126
- return unwrapValue(res);
145
+ return unwrapJsonValue(res);
127
146
  };
128
147
  const guestSnap = async (q, p) => {
129
148
  const res = await (isFunction(nova.snap) ? nova.snap(q, p) : nova.call("snap", { query: q, path: p }));
130
- return unwrapValue(res);
149
+ return unwrapJsonValue(res);
131
150
  };
132
151
  const guestBash = async (cmd, opts) => {
133
152
  const res = await nova.call("bash", { command: cmd, ...opts });
package/snap.js CHANGED
@@ -94,21 +94,46 @@ function scoreContentDefinitions(content, tokens) {
94
94
  return { totalScore: score, bestLine, bestLineScore };
95
95
  }
96
96
 
97
- export async function executeSnap({ query, searchDir, vfs, runCommand }) {
97
+ function relativeHasSegment(relativePath, segmentName) {
98
+ return relativePath.split(path.sep).includes(segmentName);
99
+ }
100
+
101
+ function relativeHasHiddenSegment(relativePath) {
102
+ return relativePath.split(path.sep).some((segment) => segment.startsWith(".") && segment.length > 1);
103
+ }
104
+
105
+ export async function executeSnap({ query, searchDir, includeHidden = false, vfs, runCommand, pendingPaths = [] }) {
98
106
  const { tokens, wantsTest, wantsType, wantsDoc } = tokenizeQuery(query);
99
107
  if (tokens.length === 0) {
100
108
  throw new Error("snap requires at least one searchable concept keyword");
101
109
  }
102
110
 
103
111
  const dir = searchDir || process.cwd();
112
+ if (path.resolve(dir).split(path.sep).includes(".git")) {
113
+ throw new Error("snap cannot search Git metadata");
114
+ }
104
115
  let fileList = [];
105
116
  try {
106
- const res = await runCommand(["rg", "--files", dir], { timeoutMs: 15_000 });
117
+ const rgArgs = ["rg", "--files"];
118
+ if (includeHidden) rgArgs.push("--hidden");
119
+ rgArgs.push("-g", "!.git/**", "-g", "!**/.git/**", dir);
120
+ const res = await runCommand(rgArgs, { timeoutMs: 15_000 });
107
121
  fileList = res.stdout.split("\n").map((f) => f.trim()).filter(Boolean);
108
122
  } catch {
109
123
  fileList = [];
110
124
  }
111
125
 
126
+ const seenPaths = new Set(fileList.map((filePath) => path.resolve(filePath)));
127
+ for (const pendingPath of pendingPaths) {
128
+ const absolutePath = path.resolve(pendingPath);
129
+ const relativePath = path.relative(path.resolve(dir), absolutePath);
130
+ const escapesDir = relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath);
131
+ const hiddenRelativePath = relativeHasHiddenSegment(relativePath);
132
+ if (escapesDir || relativeHasSegment(relativePath, ".git") || (!includeHidden && hiddenRelativePath) || seenPaths.has(absolutePath)) continue;
133
+ seenPaths.add(absolutePath);
134
+ fileList.push(absolutePath);
135
+ }
136
+
112
137
  if (fileList.length === 0) {
113
138
  throw new Error(`no files found to search in ${dir}`);
114
139
  }
@@ -125,7 +150,8 @@ export async function executeSnap({ query, searchDir, vfs, runCommand }) {
125
150
 
126
151
  if (candidates.length < 5) {
127
152
  try {
128
- const grepArgs = ["-l", "--max-count=1"];
153
+ const grepArgs = ["-l", "--max-count=1", "-g", "!.git/**", "-g", "!**/.git/**"];
154
+ if (includeHidden) grepArgs.push("--hidden");
129
155
  if (!wantsTest) {
130
156
  grepArgs.push("-g", "!test/**", "-g", "!tests/**", "-g", "!*.test.*", "-g", "!*.spec.*");
131
157
  }