prior-cli 1.7.11 → 1.7.12
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/bin/prior.js +47 -0
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -230,6 +230,30 @@ function expandFileRefs(input, cwd) {
|
|
|
230
230
|
return { message: `${input}\n\n[Attached file context]\n${ctx}`, attached };
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// Live completions for an @file token being typed. `partial` is the text
|
|
234
|
+
// after @ (e.g. "lib/to"). Returns up to `limit` matches, dirs first.
|
|
235
|
+
const REF_SKIP_DIRS = new Set(['node_modules', '.git', '.next', 'dist', 'build', '.cache', 'coverage', 'vendor']);
|
|
236
|
+
function fileRefSuggestions(partial, cwd, limit = 6) {
|
|
237
|
+
const slash = Math.max(partial.lastIndexOf('/'), partial.lastIndexOf('\\'));
|
|
238
|
+
const dirPart = slash >= 0 ? partial.slice(0, slash) : '';
|
|
239
|
+
const prefix = (slash >= 0 ? partial.slice(slash + 1) : partial).toLowerCase();
|
|
240
|
+
const baseDir = dirPart
|
|
241
|
+
? (path.isAbsolute(dirPart) ? dirPart : path.resolve(cwd, dirPart))
|
|
242
|
+
: cwd;
|
|
243
|
+
let entries;
|
|
244
|
+
try { entries = fs.readdirSync(baseDir, { withFileTypes: true }); } catch { return []; }
|
|
245
|
+
const out = [];
|
|
246
|
+
for (const e of entries) {
|
|
247
|
+
if (e.name.startsWith('.') && !prefix.startsWith('.')) continue; // hide dotfiles unless asked
|
|
248
|
+
if (e.isDirectory() && REF_SKIP_DIRS.has(e.name)) continue;
|
|
249
|
+
if (!e.name.toLowerCase().startsWith(prefix)) continue;
|
|
250
|
+
const ref = (dirPart ? dirPart.replace(/\\/g, '/') + '/' : '') + e.name;
|
|
251
|
+
out.push({ ref, isDir: e.isDirectory() });
|
|
252
|
+
}
|
|
253
|
+
out.sort((a, b) => (a.isDir === b.isDir ? a.ref.localeCompare(b.ref) : a.isDir ? -1 : 1));
|
|
254
|
+
return out.slice(0, limit);
|
|
255
|
+
}
|
|
256
|
+
|
|
233
257
|
function fmtElapsed(ms) {
|
|
234
258
|
const s = Math.floor(ms / 1000);
|
|
235
259
|
if (s < 60) return `${s}s`;
|
|
@@ -863,6 +887,13 @@ async function startChat(opts = {}) {
|
|
|
863
887
|
terminal: true,
|
|
864
888
|
historySize: 100,
|
|
865
889
|
completer: line => {
|
|
890
|
+
// @file completion — complete the @token at the end of the line
|
|
891
|
+
const at = line.match(/(?:^|\s)@([^\s]*)$/);
|
|
892
|
+
if (at) {
|
|
893
|
+
const matches = fileRefSuggestions(at[1], process.cwd(), 20)
|
|
894
|
+
.map(s => '@' + (s.isDir ? s.ref + '/' : s.ref));
|
|
895
|
+
return [matches, '@' + at[1]];
|
|
896
|
+
}
|
|
866
897
|
const cmds = ['/help', '/clear', '/censored', '/uncensored', '/login', '/logout', '/exit'];
|
|
867
898
|
if (!line.startsWith('/')) return [[], line];
|
|
868
899
|
const hits = cmds.filter(cmd => cmd.startsWith(line));
|
|
@@ -972,6 +1003,22 @@ async function startChat(opts = {}) {
|
|
|
972
1003
|
}
|
|
973
1004
|
}
|
|
974
1005
|
|
|
1006
|
+
// @file completions — when the token at the end of the line starts with @
|
|
1007
|
+
const atMatch = (line || '').match(/(?:^|\s)@([^\s]*)$/);
|
|
1008
|
+
if (atMatch) {
|
|
1009
|
+
const sugg = fileRefSuggestions(atMatch[1], process.cwd());
|
|
1010
|
+
for (const s of sugg) {
|
|
1011
|
+
const label = s.isDir ? s.ref + '/' : s.ref;
|
|
1012
|
+
const icon = s.isDir ? '📁' : '📄';
|
|
1013
|
+
process.stdout.write(`\x1b[B\r\x1b[2K ${c.brand('@')}${c.white(label.padEnd(30))}${c.dim(icon)}`);
|
|
1014
|
+
rows++;
|
|
1015
|
+
}
|
|
1016
|
+
if (sugg.length) {
|
|
1017
|
+
process.stdout.write(`\x1b[B\r\x1b[2K ${c.dim('tab to complete · attaches file contents as context')}`);
|
|
1018
|
+
rows++;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
975
1022
|
process.stdout.write('\x1b[u');
|
|
976
1023
|
_subRowCount = rows;
|
|
977
1024
|
}
|