mu-harness 0.16.17 → 0.16.20
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/esm/tui/chat/picker.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export interface Candidate {
|
|
|
4
4
|
kind: 'file' | 'agent';
|
|
5
5
|
}
|
|
6
6
|
export declare function collectCandidates(cwd: string, agentNames: string[]): Candidate[];
|
|
7
|
-
export declare function invalidateCandidates(): void;
|
|
8
7
|
export declare function rank(query: string, candidates: Candidate[], limit?: number): Candidate[];
|
|
9
8
|
export interface ActiveMention {
|
|
10
9
|
start: number;
|
package/esm/tui/chat/picker.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
1
2
|
import { readdirSync, statSync } from 'node:fs';
|
|
2
3
|
import { join, relative } from 'node:path';
|
|
3
4
|
const IGNORED = new Set([
|
|
@@ -19,7 +20,21 @@ const IGNORED = new Set([
|
|
|
19
20
|
]);
|
|
20
21
|
const MAX_ENTRIES = 5000;
|
|
21
22
|
const MAX_DEPTH = 6;
|
|
22
|
-
|
|
23
|
+
function gitFiles(cwd) {
|
|
24
|
+
try {
|
|
25
|
+
const out = execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], {
|
|
26
|
+
cwd,
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
29
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
+
});
|
|
31
|
+
const files = out.split('\0').filter((f) => f.length > 0);
|
|
32
|
+
return files.length > 0 ? files.slice(0, MAX_ENTRIES).sort() : undefined;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
23
38
|
function walk(cwd) {
|
|
24
39
|
const out = [];
|
|
25
40
|
const stack = [{ dir: cwd, depth: 0 }];
|
|
@@ -59,30 +74,41 @@ function walk(cwd) {
|
|
|
59
74
|
return out.sort();
|
|
60
75
|
}
|
|
61
76
|
export function collectCandidates(cwd, agentNames) {
|
|
62
|
-
|
|
63
|
-
cache = { cwd, files: walk(cwd) };
|
|
77
|
+
const paths = gitFiles(cwd) ?? walk(cwd);
|
|
64
78
|
const agents = agentNames.map((name) => ({ label: `@${name}`, insert: name, kind: 'agent' }));
|
|
65
|
-
const files =
|
|
79
|
+
const files = paths.map((path) => ({ label: path, insert: path, kind: 'file' }));
|
|
66
80
|
return [...agents, ...files];
|
|
67
81
|
}
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
function isBoundary(target, i) {
|
|
83
|
+
if (i === 0)
|
|
84
|
+
return true;
|
|
85
|
+
const prev = target[i - 1] ?? '';
|
|
86
|
+
if (/[/\\_\-. ]/.test(prev))
|
|
87
|
+
return true;
|
|
88
|
+
const cur = target[i] ?? '';
|
|
89
|
+
return prev === prev.toLowerCase() && prev !== prev.toUpperCase() && cur === cur.toUpperCase() &&
|
|
90
|
+
cur !== cur.toLowerCase();
|
|
70
91
|
}
|
|
71
|
-
function
|
|
92
|
+
function fuzzyScore(query, target) {
|
|
72
93
|
if (query.length === 0)
|
|
73
94
|
return 0;
|
|
74
95
|
const q = query.toLowerCase();
|
|
75
|
-
const t = target.toLowerCase();
|
|
76
96
|
let qi = 0;
|
|
77
97
|
let total = 0;
|
|
78
98
|
let prev = -2;
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
let run = 0;
|
|
100
|
+
for (let i = 0; i < target.length && qi < q.length; i++) {
|
|
101
|
+
if (target[i].toLowerCase() === q[qi]) {
|
|
81
102
|
let s = 1;
|
|
82
|
-
if (i === prev + 1)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
if (i === prev + 1) {
|
|
104
|
+
run += 1;
|
|
105
|
+
s += 2 + run;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
run = 0;
|
|
109
|
+
}
|
|
110
|
+
if (isBoundary(target, i))
|
|
111
|
+
s += 4;
|
|
86
112
|
total += s;
|
|
87
113
|
prev = i;
|
|
88
114
|
qi += 1;
|
|
@@ -90,14 +116,40 @@ function score(query, target) {
|
|
|
90
116
|
}
|
|
91
117
|
if (qi < q.length)
|
|
92
118
|
return undefined;
|
|
93
|
-
return total
|
|
119
|
+
return total;
|
|
120
|
+
}
|
|
121
|
+
function basename(path) {
|
|
122
|
+
const cut = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
|
123
|
+
return cut === -1 ? path : path.slice(cut + 1);
|
|
124
|
+
}
|
|
125
|
+
function score(query, candidate) {
|
|
126
|
+
if (query.length === 0)
|
|
127
|
+
return 0;
|
|
128
|
+
const pathScore = fuzzyScore(query, candidate.label);
|
|
129
|
+
if (pathScore === undefined)
|
|
130
|
+
return undefined;
|
|
131
|
+
let total = pathScore;
|
|
132
|
+
if (candidate.kind === 'file') {
|
|
133
|
+
const base = basename(candidate.label);
|
|
134
|
+
const baseScore = fuzzyScore(query, base);
|
|
135
|
+
if (baseScore !== undefined) {
|
|
136
|
+
total += baseScore * 2;
|
|
137
|
+
const lb = base.toLowerCase();
|
|
138
|
+
const lq = query.toLowerCase();
|
|
139
|
+
if (lb === lq)
|
|
140
|
+
total += 100;
|
|
141
|
+
else if (lb.startsWith(lq))
|
|
142
|
+
total += 40;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return total - candidate.label.length * 0.05;
|
|
94
146
|
}
|
|
95
147
|
export function rank(query, candidates, limit = 8) {
|
|
96
148
|
if (!query)
|
|
97
149
|
return candidates.slice(0, limit);
|
|
98
150
|
const scored = [];
|
|
99
151
|
for (const candidate of candidates) {
|
|
100
|
-
const s = score(query, candidate
|
|
152
|
+
const s = score(query, candidate);
|
|
101
153
|
if (s !== undefined)
|
|
102
154
|
scored.push({ candidate, score: s });
|
|
103
155
|
}
|
|
@@ -176,7 +176,7 @@ export function formatToolArgs(name, input, max = 120) {
|
|
|
176
176
|
if (input === null || typeof input !== 'object')
|
|
177
177
|
return truncateText(String(input ?? ''), max);
|
|
178
178
|
const args = input;
|
|
179
|
-
if (name === 'edit' || name === 'write' || name === 'read' || name === '
|
|
179
|
+
if (name === 'edit' || name === 'write' || name === 'read' || name === 'list') {
|
|
180
180
|
return truncateText(stringifyArg(args.path), max);
|
|
181
181
|
}
|
|
182
182
|
if (name === 'bash')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mu-harness",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.20",
|
|
4
4
|
"description": "Agent harness: createHarness wires mu-core into a host — XDG paths, model registry, plugins, disk-loaded agents & skills, sub-agents, sessions (JSONL + SQLite catalog), slash commands, permission/approval hooks, an optional scheduler, and a composable TUI chat app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./script/index.js",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"@swc/wasm-typescript": "^1.15.0",
|
|
24
24
|
"cli-highlight": "^2.1.11",
|
|
25
25
|
"croner": "^9.0.0",
|
|
26
|
-
"mu-core": "^0.16.
|
|
27
|
-
"mu-tui": "^0.16.
|
|
26
|
+
"mu-core": "^0.16.20",
|
|
27
|
+
"mu-tui": "^0.16.20"
|
|
28
28
|
},
|
|
29
29
|
"_generatedBy": "dnt@dev",
|
|
30
30
|
"types": "./esm/index.d.ts"
|
|
@@ -4,7 +4,6 @@ export interface Candidate {
|
|
|
4
4
|
kind: 'file' | 'agent';
|
|
5
5
|
}
|
|
6
6
|
export declare function collectCandidates(cwd: string, agentNames: string[]): Candidate[];
|
|
7
|
-
export declare function invalidateCandidates(): void;
|
|
8
7
|
export declare function rank(query: string, candidates: Candidate[], limit?: number): Candidate[];
|
|
9
8
|
export interface ActiveMention {
|
|
10
9
|
start: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.collectCandidates = collectCandidates;
|
|
4
|
-
exports.invalidateCandidates = invalidateCandidates;
|
|
5
4
|
exports.rank = rank;
|
|
6
5
|
exports.activeMention = activeMention;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const IGNORED = new Set([
|
|
@@ -25,7 +25,21 @@ const IGNORED = new Set([
|
|
|
25
25
|
]);
|
|
26
26
|
const MAX_ENTRIES = 5000;
|
|
27
27
|
const MAX_DEPTH = 6;
|
|
28
|
-
|
|
28
|
+
function gitFiles(cwd) {
|
|
29
|
+
try {
|
|
30
|
+
const out = (0, node_child_process_1.execFileSync)('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], {
|
|
31
|
+
cwd,
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
34
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
35
|
+
});
|
|
36
|
+
const files = out.split('\0').filter((f) => f.length > 0);
|
|
37
|
+
return files.length > 0 ? files.slice(0, MAX_ENTRIES).sort() : undefined;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
29
43
|
function walk(cwd) {
|
|
30
44
|
const out = [];
|
|
31
45
|
const stack = [{ dir: cwd, depth: 0 }];
|
|
@@ -65,30 +79,41 @@ function walk(cwd) {
|
|
|
65
79
|
return out.sort();
|
|
66
80
|
}
|
|
67
81
|
function collectCandidates(cwd, agentNames) {
|
|
68
|
-
|
|
69
|
-
cache = { cwd, files: walk(cwd) };
|
|
82
|
+
const paths = gitFiles(cwd) ?? walk(cwd);
|
|
70
83
|
const agents = agentNames.map((name) => ({ label: `@${name}`, insert: name, kind: 'agent' }));
|
|
71
|
-
const files =
|
|
84
|
+
const files = paths.map((path) => ({ label: path, insert: path, kind: 'file' }));
|
|
72
85
|
return [...agents, ...files];
|
|
73
86
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
87
|
+
function isBoundary(target, i) {
|
|
88
|
+
if (i === 0)
|
|
89
|
+
return true;
|
|
90
|
+
const prev = target[i - 1] ?? '';
|
|
91
|
+
if (/[/\\_\-. ]/.test(prev))
|
|
92
|
+
return true;
|
|
93
|
+
const cur = target[i] ?? '';
|
|
94
|
+
return prev === prev.toLowerCase() && prev !== prev.toUpperCase() && cur === cur.toUpperCase() &&
|
|
95
|
+
cur !== cur.toLowerCase();
|
|
76
96
|
}
|
|
77
|
-
function
|
|
97
|
+
function fuzzyScore(query, target) {
|
|
78
98
|
if (query.length === 0)
|
|
79
99
|
return 0;
|
|
80
100
|
const q = query.toLowerCase();
|
|
81
|
-
const t = target.toLowerCase();
|
|
82
101
|
let qi = 0;
|
|
83
102
|
let total = 0;
|
|
84
103
|
let prev = -2;
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
let run = 0;
|
|
105
|
+
for (let i = 0; i < target.length && qi < q.length; i++) {
|
|
106
|
+
if (target[i].toLowerCase() === q[qi]) {
|
|
87
107
|
let s = 1;
|
|
88
|
-
if (i === prev + 1)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
if (i === prev + 1) {
|
|
109
|
+
run += 1;
|
|
110
|
+
s += 2 + run;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
run = 0;
|
|
114
|
+
}
|
|
115
|
+
if (isBoundary(target, i))
|
|
116
|
+
s += 4;
|
|
92
117
|
total += s;
|
|
93
118
|
prev = i;
|
|
94
119
|
qi += 1;
|
|
@@ -96,14 +121,40 @@ function score(query, target) {
|
|
|
96
121
|
}
|
|
97
122
|
if (qi < q.length)
|
|
98
123
|
return undefined;
|
|
99
|
-
return total
|
|
124
|
+
return total;
|
|
125
|
+
}
|
|
126
|
+
function basename(path) {
|
|
127
|
+
const cut = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
|
128
|
+
return cut === -1 ? path : path.slice(cut + 1);
|
|
129
|
+
}
|
|
130
|
+
function score(query, candidate) {
|
|
131
|
+
if (query.length === 0)
|
|
132
|
+
return 0;
|
|
133
|
+
const pathScore = fuzzyScore(query, candidate.label);
|
|
134
|
+
if (pathScore === undefined)
|
|
135
|
+
return undefined;
|
|
136
|
+
let total = pathScore;
|
|
137
|
+
if (candidate.kind === 'file') {
|
|
138
|
+
const base = basename(candidate.label);
|
|
139
|
+
const baseScore = fuzzyScore(query, base);
|
|
140
|
+
if (baseScore !== undefined) {
|
|
141
|
+
total += baseScore * 2;
|
|
142
|
+
const lb = base.toLowerCase();
|
|
143
|
+
const lq = query.toLowerCase();
|
|
144
|
+
if (lb === lq)
|
|
145
|
+
total += 100;
|
|
146
|
+
else if (lb.startsWith(lq))
|
|
147
|
+
total += 40;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return total - candidate.label.length * 0.05;
|
|
100
151
|
}
|
|
101
152
|
function rank(query, candidates, limit = 8) {
|
|
102
153
|
if (!query)
|
|
103
154
|
return candidates.slice(0, limit);
|
|
104
155
|
const scored = [];
|
|
105
156
|
for (const candidate of candidates) {
|
|
106
|
-
const s = score(query, candidate
|
|
157
|
+
const s = score(query, candidate);
|
|
107
158
|
if (s !== undefined)
|
|
108
159
|
scored.push({ candidate, score: s });
|
|
109
160
|
}
|
|
@@ -183,7 +183,7 @@ function formatToolArgs(name, input, max = 120) {
|
|
|
183
183
|
if (input === null || typeof input !== 'object')
|
|
184
184
|
return truncateText(String(input ?? ''), max);
|
|
185
185
|
const args = input;
|
|
186
|
-
if (name === 'edit' || name === 'write' || name === 'read' || name === '
|
|
186
|
+
if (name === 'edit' || name === 'write' || name === 'read' || name === 'list') {
|
|
187
187
|
return truncateText(stringifyArg(args.path), max);
|
|
188
188
|
}
|
|
189
189
|
if (name === 'bash')
|