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.
Files changed (2) hide show
  1. package/bin/prior.js +47 -0
  2. 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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.7.11",
3
+ "version": "1.7.12",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "author": "Prior Network",
6
6
  "homepage": "https://priornetwork.com",