gnosys 5.12.0 → 5.12.3
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/cli.js +48 -7
- package/dist/index.js +199 -14
- package/dist/lib/addCommand.js +0 -1
- package/dist/lib/archive.js +10 -6
- package/dist/lib/askCommand.js +1 -1
- package/dist/lib/attachCommand.d.ts +17 -0
- package/dist/lib/attachCommand.js +66 -0
- package/dist/lib/attachments.d.ts +43 -2
- package/dist/lib/attachments.js +81 -2
- package/dist/lib/chat/choose.js +2 -2
- package/dist/lib/clientReadOverlay.js +3 -0
- package/dist/lib/config.d.ts +1 -48
- package/dist/lib/configCommand.js +2 -2
- package/dist/lib/db.d.ts +16 -1
- package/dist/lib/db.js +248 -133
- package/dist/lib/dbWrite.d.ts +1 -1
- package/dist/lib/dearchiveCommand.js +1 -1
- package/dist/lib/docxExtract.js +1 -1
- package/dist/lib/dream.d.ts +8 -0
- package/dist/lib/dream.js +35 -1
- package/dist/lib/dreamLogCommand.js +1 -1
- package/dist/lib/dreamRunLog.d.ts +1 -1
- package/dist/lib/dreamRunLog.js +26 -4
- package/dist/lib/embeddings.js +0 -3
- package/dist/lib/exportProject.d.ts +3 -2
- package/dist/lib/exportProject.js +2 -1
- package/dist/lib/federated.js +1 -1
- package/dist/lib/ftsQuery.d.ts +25 -0
- package/dist/lib/ftsQuery.js +42 -0
- package/dist/lib/hybridSearch.d.ts +9 -0
- package/dist/lib/hybridSearch.js +14 -0
- package/dist/lib/hybridSearchCommand.js +6 -1
- package/dist/lib/importProject.js +2 -1
- package/dist/lib/llm.js +1 -1
- package/dist/lib/lock.d.ts +1 -1
- package/dist/lib/lock.js +5 -3
- package/dist/lib/migrate.js +0 -1
- package/dist/lib/multimodalIngest.js +1 -1
- package/dist/lib/platform.d.ts +0 -6
- package/dist/lib/platform.js +0 -28
- package/dist/lib/readCommand.js +11 -10
- package/dist/lib/remoteWizard.d.ts +1 -1
- package/dist/lib/remoteWizard.js +4 -4
- package/dist/lib/resolver.d.ts +17 -0
- package/dist/lib/resolver.js +43 -17
- package/dist/lib/rulesGen.d.ts +8 -0
- package/dist/lib/rulesGen.js +16 -0
- package/dist/lib/search.d.ts +0 -2
- package/dist/lib/search.js +33 -27
- package/dist/lib/semanticSearchCommand.js +1 -1
- package/dist/lib/setup/sections/providers.js +56 -4
- package/dist/lib/setup/sections/routing.js +42 -5
- package/dist/lib/setup/sections/taskRoutingEditor.d.ts +1 -5
- package/dist/lib/setup/sections/taskRoutingEditor.js +0 -10
- package/dist/lib/setup/ui/header.js +0 -1
- package/dist/lib/setup/ui/status.d.ts +0 -1
- package/dist/lib/setup/ui/status.js +0 -2
- package/dist/lib/setup.d.ts +0 -15
- package/dist/lib/setup.js +13 -158
- package/dist/lib/staleCommand.js +2 -2
- package/dist/lib/syncClient.d.ts +0 -6
- package/dist/lib/syncClient.js +36 -14
- package/dist/lib/syncDoctorCommand.js +2 -2
- package/dist/lib/syncIngest.d.ts +11 -0
- package/dist/lib/syncIngest.js +24 -1
- package/dist/lib/syncIngestStartup.js +2 -2
- package/dist/lib/syncSnapshot.d.ts +2 -0
- package/dist/lib/syncSnapshot.js +4 -0
- package/dist/lib/syncStaging.d.ts +0 -2
- package/dist/lib/syncStaging.js +0 -2
- package/dist/lib/updateCommand.js +1 -1
- package/dist/lib/webBuildCommand.js +1 -1
- package/dist/lib/webIndex.js +0 -1
- package/dist/lib/webIngestCommand.js +1 -1
- package/dist/sandbox/client.js +1 -1
- package/dist/sandbox/manager.js +1 -14
- package/dist/sandbox/server.js +3 -5
- package/package.json +5 -2
package/dist/lib/resolver.js
CHANGED
|
@@ -79,8 +79,13 @@ export class GnosysResolver {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
catch {
|
|
82
|
-
//
|
|
82
|
+
// No project store at projectRoot — still load the env tiers below.
|
|
83
83
|
}
|
|
84
|
+
// Even when scoped to a projectRoot, load the personal/global/optional
|
|
85
|
+
// tiers so cross-project + global memories stay reachable (write store:
|
|
86
|
+
// "global"/"personal", and read them back from any project). Without this,
|
|
87
|
+
// the universal "always pass projectRoot" guidance silently disabled them.
|
|
88
|
+
await resolver.loadEnvTiers();
|
|
84
89
|
return resolver;
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
@@ -104,7 +109,30 @@ export class GnosysResolver {
|
|
|
104
109
|
path: projectPath,
|
|
105
110
|
});
|
|
106
111
|
}
|
|
107
|
-
// 2.
|
|
112
|
+
// 2-4. Env-configured tiers (optional, personal, global). Shared with
|
|
113
|
+
// resolveForProject so passing a projectRoot does NOT strip the
|
|
114
|
+
// cross-project tiers.
|
|
115
|
+
await this.loadEnvTiers();
|
|
116
|
+
return this.stores;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Load the env-configured store tiers (optional `GNOSYS_STORES`, personal
|
|
120
|
+
* `GNOSYS_PERSONAL`, global `GNOSYS_GLOBAL`) onto `this.stores`.
|
|
121
|
+
*
|
|
122
|
+
* Shared by {@link resolve} and {@link resolveForProject}: a per-tool
|
|
123
|
+
* `projectRoot` call must STILL see personal/global, otherwise cross-project
|
|
124
|
+
* and global memories are unreachable — you couldn't write `store: "global"`
|
|
125
|
+
* / `"personal"` nor read them back from another project. Since every tool
|
|
126
|
+
* is told to always pass `projectRoot`, omitting these here silently
|
|
127
|
+
* disabled the entire cross-project tier system.
|
|
128
|
+
*
|
|
129
|
+
* Global is loaded whenever `GNOSYS_GLOBAL` is set, creating its directory on
|
|
130
|
+
* demand (parity with personal) so a configured-but-empty global store still
|
|
131
|
+
* works. It remains write-only-when-explicitly-targeted: {@link getWriteTarget}
|
|
132
|
+
* never auto-selects it.
|
|
133
|
+
*/
|
|
134
|
+
async loadEnvTiers() {
|
|
135
|
+
// Optional stores (GNOSYS_STORES — colon-separated, read-only)
|
|
108
136
|
const optionalPaths = process.env.GNOSYS_STORES;
|
|
109
137
|
if (optionalPaths) {
|
|
110
138
|
const paths = optionalPaths.split(":").filter(Boolean);
|
|
@@ -124,7 +152,7 @@ export class GnosysResolver {
|
|
|
124
152
|
}
|
|
125
153
|
}
|
|
126
154
|
}
|
|
127
|
-
//
|
|
155
|
+
// Personal store (GNOSYS_PERSONAL — writable fallback target)
|
|
128
156
|
const personalPath = process.env.GNOSYS_PERSONAL;
|
|
129
157
|
if (personalPath) {
|
|
130
158
|
const p = path.resolve(personalPath);
|
|
@@ -138,24 +166,22 @@ export class GnosysResolver {
|
|
|
138
166
|
path: p,
|
|
139
167
|
});
|
|
140
168
|
}
|
|
141
|
-
//
|
|
142
|
-
//
|
|
169
|
+
// Global store (GNOSYS_GLOBAL — writable, but never auto-selected). Loaded
|
|
170
|
+
// whenever set; store.init() creates the dir on demand (parity with
|
|
171
|
+
// personal), so a configured-but-empty GNOSYS_GLOBAL still works.
|
|
143
172
|
const globalPath = process.env.GNOSYS_GLOBAL;
|
|
144
173
|
if (globalPath) {
|
|
145
174
|
const p = path.resolve(globalPath);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
});
|
|
156
|
-
}
|
|
175
|
+
const store = new GnosysStore(p);
|
|
176
|
+
await store.init();
|
|
177
|
+
this.stores.push({
|
|
178
|
+
layer: "global",
|
|
179
|
+
label: "global",
|
|
180
|
+
store,
|
|
181
|
+
writable: true,
|
|
182
|
+
path: p,
|
|
183
|
+
});
|
|
157
184
|
}
|
|
158
|
-
return this.stores;
|
|
159
185
|
}
|
|
160
186
|
/**
|
|
161
187
|
* Get all stores in precedence order.
|
package/dist/lib/rulesGen.d.ts
CHANGED
|
@@ -47,6 +47,14 @@ export declare function syncRules(centralDb: GnosysDB, projectDir: string, agent
|
|
|
47
47
|
* If target is "global", syncs to ~/.claude/CLAUDE.md.
|
|
48
48
|
*/
|
|
49
49
|
export declare function syncToTarget(centralDb: GnosysDB, projectDir: string, target: string, projectId: string | null): Promise<RulesGenResult[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Remove the GNOSYS block from every known agent rules file in a project —
|
|
52
|
+
* the uninstall counterpart of syncToTarget (v5.12.1: wired into
|
|
53
|
+
* `gnosys cleanup --rules`). Checks every known target rather than only
|
|
54
|
+
* detected ones, so leftovers survive even if the agent dir was removed.
|
|
55
|
+
* Returns the relative paths actually cleaned.
|
|
56
|
+
*/
|
|
57
|
+
export declare function removeRulesFromProject(projectDir: string): Promise<string[]>;
|
|
50
58
|
/**
|
|
51
59
|
* Remove the GNOSYS block from a rules file (cleanup).
|
|
52
60
|
*/
|
package/dist/lib/rulesGen.js
CHANGED
|
@@ -254,6 +254,22 @@ export async function syncToTarget(centralDb, projectDir, target, projectId) {
|
|
|
254
254
|
}
|
|
255
255
|
return results;
|
|
256
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Remove the GNOSYS block from every known agent rules file in a project —
|
|
259
|
+
* the uninstall counterpart of syncToTarget (v5.12.1: wired into
|
|
260
|
+
* `gnosys cleanup --rules`). Checks every known target rather than only
|
|
261
|
+
* detected ones, so leftovers survive even if the agent dir was removed.
|
|
262
|
+
* Returns the relative paths actually cleaned.
|
|
263
|
+
*/
|
|
264
|
+
export async function removeRulesFromProject(projectDir) {
|
|
265
|
+
const cleaned = [];
|
|
266
|
+
for (const relPath of Object.values(TARGET_PATHS)) {
|
|
267
|
+
const filePath = path.join(projectDir, relPath);
|
|
268
|
+
if (await removeRulesBlock(filePath))
|
|
269
|
+
cleaned.push(relPath);
|
|
270
|
+
}
|
|
271
|
+
return cleaned;
|
|
272
|
+
}
|
|
257
273
|
/**
|
|
258
274
|
* Remove the GNOSYS block from a rules file (cleanup).
|
|
259
275
|
*/
|
package/dist/lib/search.d.ts
CHANGED
package/dist/lib/search.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* FTS5-based search and discovery across all Gnosys stores.
|
|
4
4
|
*/
|
|
5
5
|
// Dynamic import — gracefully handles missing native module (dlopen failures)
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
6
|
let Database = null;
|
|
8
7
|
try {
|
|
9
8
|
Database = (await import("better-sqlite3")).default;
|
|
@@ -12,13 +11,10 @@ catch {
|
|
|
12
11
|
// better-sqlite3 native module not available — search degrades gracefully
|
|
13
12
|
}
|
|
14
13
|
import path from "path";
|
|
14
|
+
import { ftsTerms, ftsAndQuery, ftsOrQuery } from "./ftsQuery.js";
|
|
15
15
|
export class GnosysSearch {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
16
|
db = null;
|
|
18
|
-
storePath;
|
|
19
|
-
available = false;
|
|
20
17
|
constructor(storePath) {
|
|
21
|
-
this.storePath = storePath;
|
|
22
18
|
if (!Database) {
|
|
23
19
|
// Native module not available — search features disabled
|
|
24
20
|
return;
|
|
@@ -34,7 +30,6 @@ export class GnosysSearch {
|
|
|
34
30
|
this.initSchema();
|
|
35
31
|
// Smoke-test: insert + delete to confirm journal ops work
|
|
36
32
|
this.db.exec("CREATE TABLE IF NOT EXISTS _write_test (v INTEGER); INSERT INTO _write_test VALUES (1); DELETE FROM _write_test; DROP TABLE _write_test;");
|
|
37
|
-
this.available = true;
|
|
38
33
|
}
|
|
39
34
|
catch {
|
|
40
35
|
// Fallback to in-memory (works everywhere, rebuilt on each start)
|
|
@@ -44,7 +39,6 @@ export class GnosysSearch {
|
|
|
44
39
|
catch { /* ignore */ }
|
|
45
40
|
this.db = new Database(":memory:");
|
|
46
41
|
this.initSchema();
|
|
47
|
-
this.available = true;
|
|
48
42
|
}
|
|
49
43
|
}
|
|
50
44
|
initSchema() {
|
|
@@ -140,9 +134,8 @@ export class GnosysSearch {
|
|
|
140
134
|
search(query, limit = 20) {
|
|
141
135
|
if (!this.db)
|
|
142
136
|
return [];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!safeQuery)
|
|
137
|
+
const terms = ftsTerms(query);
|
|
138
|
+
if (terms.length === 0)
|
|
146
139
|
return [];
|
|
147
140
|
const stmt = this.db.prepare(`
|
|
148
141
|
SELECT
|
|
@@ -156,7 +149,12 @@ export class GnosysSearch {
|
|
|
156
149
|
LIMIT ?
|
|
157
150
|
`);
|
|
158
151
|
try {
|
|
159
|
-
|
|
152
|
+
// v5.12.3: AND first (precision), OR retry when AND finds nothing —
|
|
153
|
+
// multi-word queries previously required every term to match.
|
|
154
|
+
const results = stmt.all(ftsAndQuery(terms), limit);
|
|
155
|
+
if (results.length > 0 || terms.length === 1)
|
|
156
|
+
return results;
|
|
157
|
+
return stmt.all(ftsOrQuery(terms), limit);
|
|
160
158
|
}
|
|
161
159
|
catch {
|
|
162
160
|
// If FTS5 query fails, fall back to simple LIKE search
|
|
@@ -170,7 +168,7 @@ export class GnosysSearch {
|
|
|
170
168
|
WHERE content LIKE ? OR title LIKE ? OR tags LIKE ?
|
|
171
169
|
LIMIT ?
|
|
172
170
|
`);
|
|
173
|
-
const pattern = `%${
|
|
171
|
+
const pattern = `%${terms.join(" ")}%`;
|
|
174
172
|
return likeStmt.all(pattern, pattern, pattern, limit);
|
|
175
173
|
}
|
|
176
174
|
}
|
|
@@ -182,8 +180,8 @@ export class GnosysSearch {
|
|
|
182
180
|
discover(query, limit = 20) {
|
|
183
181
|
if (!this.db)
|
|
184
182
|
return [];
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
183
|
+
const terms = ftsTerms(query);
|
|
184
|
+
if (terms.length === 0)
|
|
187
185
|
return [];
|
|
188
186
|
// Search primarily on relevance + title + tags (not content body)
|
|
189
187
|
// FTS5 column filter: {relevance title tags}
|
|
@@ -198,24 +196,32 @@ export class GnosysSearch {
|
|
|
198
196
|
ORDER BY rank
|
|
199
197
|
LIMIT ?
|
|
200
198
|
`);
|
|
201
|
-
|
|
202
|
-
// Try column-filtered search on relevance/title/tags first
|
|
203
|
-
const colQuery = `{relevance title tags} : ${safeQuery}`;
|
|
204
|
-
const results = stmt.all(colQuery, limit);
|
|
205
|
-
if (results.length > 0)
|
|
206
|
-
return results;
|
|
207
|
-
// Fall back to full-text search if column filter finds nothing
|
|
208
|
-
return stmt.all(safeQuery, limit);
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
// If FTS5 column filter syntax fails, fall back to full search
|
|
199
|
+
const tryRun = (match) => {
|
|
212
200
|
try {
|
|
213
|
-
return stmt.all(
|
|
201
|
+
return stmt.all(match, limit);
|
|
214
202
|
}
|
|
215
203
|
catch {
|
|
216
204
|
return [];
|
|
217
205
|
}
|
|
218
|
-
}
|
|
206
|
+
};
|
|
207
|
+
// v5.12.3: precision-to-recall ladder. AND on the metadata columns,
|
|
208
|
+
// AND anywhere, then OR retries — multi-word queries previously
|
|
209
|
+
// required every term to match, so long queries returned nothing.
|
|
210
|
+
// Parens scope the column filter to the whole expression (a bare
|
|
211
|
+
// `{cols} : a b` only filtered the first term).
|
|
212
|
+
const colScoped = (expr) => `{relevance title tags} : (${expr})`;
|
|
213
|
+
const andExpr = ftsAndQuery(terms);
|
|
214
|
+
let results = tryRun(colScoped(andExpr));
|
|
215
|
+
if (results.length > 0)
|
|
216
|
+
return results;
|
|
217
|
+
results = tryRun(andExpr);
|
|
218
|
+
if (results.length > 0 || terms.length === 1)
|
|
219
|
+
return results;
|
|
220
|
+
const orExpr = ftsOrQuery(terms);
|
|
221
|
+
results = tryRun(colScoped(orExpr));
|
|
222
|
+
if (results.length > 0)
|
|
223
|
+
return results;
|
|
224
|
+
return tryRun(orExpr);
|
|
219
225
|
}
|
|
220
226
|
close() {
|
|
221
227
|
this.db?.close();
|
|
@@ -24,7 +24,7 @@ export async function runSemanticSearchCommand(getResolver, query, opts) {
|
|
|
24
24
|
const { GnosysHybridSearch } = await import("./hybridSearch.js");
|
|
25
25
|
const embeddings = new GnosysEmbeddings(storePath);
|
|
26
26
|
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
27
|
-
const results = await hybridSearch.hybridSearch(query, parseInt(opts.limit), "semantic");
|
|
27
|
+
const results = await hybridSearch.hybridSearch(query, parseInt(opts.limit, 10), "semantic");
|
|
28
28
|
outputResult(!!opts.json, {
|
|
29
29
|
query,
|
|
30
30
|
count: results.length,
|
|
@@ -129,14 +129,67 @@ async function manageProviderKeys(rl, storePath, cfg, provider) {
|
|
|
129
129
|
}
|
|
130
130
|
console.log("");
|
|
131
131
|
}
|
|
132
|
+
const defaultAction = slots.length > 0 ? 2 : 0; // when they have a key, default to "make default + model"
|
|
132
133
|
const choice = await askChoice(rl, "Actions", [
|
|
133
134
|
slots.length === 0 ? "Add API key (global)" : "Rotate global API key",
|
|
134
135
|
"Delete one stored key…",
|
|
136
|
+
"Use this provider as default for everything + pick model",
|
|
135
137
|
"Back",
|
|
136
|
-
],
|
|
137
|
-
if (choice ===
|
|
138
|
+
], defaultAction);
|
|
139
|
+
if (choice === 3) {
|
|
138
140
|
return changed;
|
|
139
141
|
}
|
|
142
|
+
if (choice === 2) {
|
|
143
|
+
// "Use this provider as default for everything + pick model"
|
|
144
|
+
// Set it as the global default provider
|
|
145
|
+
const current = await loadConfig(storePath);
|
|
146
|
+
await updateConfig(storePath, {
|
|
147
|
+
llm: {
|
|
148
|
+
...current.llm,
|
|
149
|
+
defaultProvider: provider,
|
|
150
|
+
},
|
|
151
|
+
// Clear per-task overrides so this truly becomes the single default for everything
|
|
152
|
+
taskModels: {},
|
|
153
|
+
});
|
|
154
|
+
printStatus("ok", `default provider set to ${provider} (all tasks now default to it)`);
|
|
155
|
+
// Immediately let them pick a model for it (using the same picker the main flow uses)
|
|
156
|
+
try {
|
|
157
|
+
const { fetchDynamicModels } = await import("../../setup.js");
|
|
158
|
+
const { pickModel } = await import("../../setup.js");
|
|
159
|
+
const dynamicModels = await fetchDynamicModels();
|
|
160
|
+
const tiers = dynamicModels[provider] ?? [];
|
|
161
|
+
let chosenModel;
|
|
162
|
+
if (tiers.length > 0) {
|
|
163
|
+
chosenModel = await pickModel(rl, provider, dynamicModels, `Default model for ${provider} (used for all tasks)`, current.llm[provider]?.model);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
chosenModel = await ask(rl, `Enter default model name for ${provider}: `);
|
|
167
|
+
}
|
|
168
|
+
if (chosenModel) {
|
|
169
|
+
const after = await loadConfig(storePath);
|
|
170
|
+
await updateConfig(storePath, {
|
|
171
|
+
llm: {
|
|
172
|
+
...after.llm,
|
|
173
|
+
[provider]: {
|
|
174
|
+
...(after.llm[provider] || {}),
|
|
175
|
+
model: chosenModel,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
printStatus("ok", `default model set · ${provider} / ${chosenModel}`);
|
|
180
|
+
printStatus("progress", "all tasks will now default to this provider + model", "use task routing for per-task overrides");
|
|
181
|
+
changed = true;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
printStatus("progress", "model left unchanged — you can set it from task routing");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (_err) {
|
|
188
|
+
printStatus("warn", "could not pick model right now", "you can set a default model from the main setup → task routing");
|
|
189
|
+
}
|
|
190
|
+
cfg = await loadConfig(storePath);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
140
193
|
if (choice === 0) {
|
|
141
194
|
const service = apiKeyServiceName(provider, "global");
|
|
142
195
|
console.log("");
|
|
@@ -188,7 +241,6 @@ async function manageProviderKeys(rl, storePath, cfg, provider) {
|
|
|
188
241
|
printStatus("fail", "delete failed or entry not found");
|
|
189
242
|
}
|
|
190
243
|
}
|
|
191
|
-
continue;
|
|
192
244
|
}
|
|
193
245
|
}
|
|
194
246
|
}
|
|
@@ -207,7 +259,7 @@ export async function runProvidersSetup(opts) {
|
|
|
207
259
|
console.log("");
|
|
208
260
|
console.log(Header(["gnosys", "setup", "providers"]));
|
|
209
261
|
console.log("");
|
|
210
|
-
console.log(Title("Providers", "API keys live here
|
|
262
|
+
console.log(Title("Providers", "API keys live here. Set a global default (or per-task models) from the main setup menu."));
|
|
211
263
|
console.log("");
|
|
212
264
|
for (let i = 0; i < menuProviders.length; i++) {
|
|
213
265
|
const p = menuProviders[i];
|
|
@@ -78,14 +78,49 @@ export async function runRoutingSetup(opts) {
|
|
|
78
78
|
console.log("");
|
|
79
79
|
const choice = await askChoice(opts.rl, "What would you like to do?", [
|
|
80
80
|
"Keep current routing (no changes)",
|
|
81
|
-
"Edit tasks —
|
|
82
|
-
"
|
|
81
|
+
"Edit tasks — set the same provider + model for all tasks (simple global default)",
|
|
82
|
+
"Edit tasks — pick different providers/models for specific tasks (advanced)",
|
|
83
|
+
"Reset all task overrides to the current default (the one shown in the main setup summary)",
|
|
83
84
|
], 0);
|
|
84
85
|
if (choice === 0) {
|
|
85
86
|
console.log(`${DIM}No changes.${RESET}`);
|
|
86
87
|
return false;
|
|
87
88
|
}
|
|
88
|
-
if (choice ===
|
|
89
|
+
if (choice === 1) {
|
|
90
|
+
// Edit tasks — set the same provider + model for all tasks (simple global default)
|
|
91
|
+
console.log("");
|
|
92
|
+
printStatus("progress", "setting a single default for all tasks…");
|
|
93
|
+
try {
|
|
94
|
+
const { fetchDynamicModels } = await import("../../setup.js");
|
|
95
|
+
const { pickModel } = await import("../../setup.js");
|
|
96
|
+
const dynamicModels = await fetchDynamicModels();
|
|
97
|
+
const currentModel = getProviderModel(cfg, provider);
|
|
98
|
+
const chosenModel = await pickModel(opts.rl, provider, dynamicModels, `Default model for ${provider} (used for all tasks + dream)`, currentModel);
|
|
99
|
+
if (chosenModel && chosenModel !== currentModel) {
|
|
100
|
+
const after = await loadConfig(storePath);
|
|
101
|
+
await updateConfig(storePath, {
|
|
102
|
+
llm: {
|
|
103
|
+
...after.llm,
|
|
104
|
+
[provider]: {
|
|
105
|
+
...(after.llm[provider] || {}),
|
|
106
|
+
model: chosenModel,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
// Clear per-task overrides so everything truly uses the single default
|
|
110
|
+
taskModels: {},
|
|
111
|
+
});
|
|
112
|
+
printStatus("ok", `default set for everything · ${provider} / ${chosenModel}`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
printStatus("ok", "no change to the global default");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
printStatus("warn", "could not update default model", String(err));
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (choice === 3) {
|
|
89
124
|
const { Diff } = await import("../ui/diff.js");
|
|
90
125
|
const overridesBeingCleared = Object.entries(cfg.taskModels ?? {})
|
|
91
126
|
.filter(([, v]) => v.provider !== provider || v.model !== model)
|
|
@@ -102,16 +137,18 @@ export async function runRoutingSetup(opts) {
|
|
|
102
137
|
else {
|
|
103
138
|
console.log(`${DIM}No overrides to clear — already using default everywhere.${RESET}`);
|
|
104
139
|
}
|
|
105
|
-
|
|
140
|
+
console.log(`${DIM}The current default is ${provider} / ${model} (set via the main setup "Default provider" or the simple global option above).${RESET}`);
|
|
141
|
+
const confirmReset = await askYesNo(opts.rl, "Reset all task overrides to the current default?", true);
|
|
106
142
|
if (!confirmReset) {
|
|
107
143
|
console.log(`${DIM}Cancelled.${RESET}`);
|
|
108
144
|
return false;
|
|
109
145
|
}
|
|
110
146
|
await updateConfig(storePath, { taskModels: {} });
|
|
111
|
-
printStatus("ok", "routing reset", "all tasks use default provider/model");
|
|
147
|
+
printStatus("ok", "routing reset", "all tasks now use the global default provider/model");
|
|
112
148
|
console.log(Footer("press enter to return"));
|
|
113
149
|
return true;
|
|
114
150
|
}
|
|
151
|
+
// choice === 2 → advanced per-task editor (the powerful comma-list path)
|
|
115
152
|
const patch = await runCommaListRoutingEditor(opts.rl, storePath, cfg);
|
|
116
153
|
if (!patch) {
|
|
117
154
|
return false;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Comma-list task routing editor (provider + model per selected task).
|
|
3
3
|
*/
|
|
4
4
|
import type { Interface as ReadlineInterface } from "readline/promises";
|
|
5
|
-
import {
|
|
5
|
+
import type { GnosysConfig } from "../../config.js";
|
|
6
6
|
/**
|
|
7
7
|
* Let the user pick tasks by number, then provider + model for each.
|
|
8
8
|
* Returns a patch for taskModels + dream, or null if cancelled.
|
|
@@ -11,7 +11,3 @@ export declare function runCommaListRoutingEditor(rl: ReadlineInterface, storePa
|
|
|
11
11
|
taskModels: NonNullable<GnosysConfig["taskModels"]>;
|
|
12
12
|
dream: GnosysConfig["dream"];
|
|
13
13
|
} | null>;
|
|
14
|
-
export declare function applyRoutingPatch(projectDir: string, patch: {
|
|
15
|
-
taskModels: NonNullable<GnosysConfig["taskModels"]>;
|
|
16
|
-
dream: GnosysConfig["dream"];
|
|
17
|
-
}): Promise<GnosysConfig>;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Comma-list task routing editor (provider + model per selected task).
|
|
3
3
|
*/
|
|
4
|
-
import { loadConfig, updateConfig, } from "../../config.js";
|
|
5
4
|
import { ASSIGNABLE_TASK_LIST, fetchDynamicModels, getAssignableRouting, modelForTaskAssignment, parseCommaSeparatedTaskSelection, pickModel, pickProvider, TASK_DESCRIPTIONS, } from "../../setup.js";
|
|
6
|
-
import { resolveActiveStorePath } from "../storePath.js";
|
|
7
5
|
import { safeQuestion } from "../ui/safePrompt.js";
|
|
8
6
|
import { renderProviderMark } from "../providerGlyphs.js";
|
|
9
7
|
import { c, color, glyph } from "../ui/tokens.js";
|
|
@@ -139,11 +137,3 @@ export async function runCommaListRoutingEditor(rl, storePath, cfg) {
|
|
|
139
137
|
}
|
|
140
138
|
return { taskModels, dream };
|
|
141
139
|
}
|
|
142
|
-
export async function applyRoutingPatch(projectDir, patch) {
|
|
143
|
-
const storePath = resolveActiveStorePath(projectDir);
|
|
144
|
-
await updateConfig(storePath, {
|
|
145
|
-
taskModels: patch.taskModels,
|
|
146
|
-
dream: patch.dream,
|
|
147
|
-
});
|
|
148
|
-
return loadConfig(storePath);
|
|
149
|
-
}
|
|
@@ -39,7 +39,6 @@ export function Header(crumbs, opts = {}) {
|
|
|
39
39
|
}
|
|
40
40
|
/** Strip ANSI escapes so we can measure printable width. Exported for callers/tests that need printable measurement. */
|
|
41
41
|
export function stripAnsi(s) {
|
|
42
|
-
// eslint-disable-next-line no-control-regex
|
|
43
42
|
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
44
43
|
}
|
|
45
44
|
/** Convenience: print header + trailing blank line to stdout. */
|
|
@@ -18,4 +18,3 @@ export type StatusKind = "ok" | "warn" | "fail" | "progress";
|
|
|
18
18
|
export declare function Status(kind: StatusKind, text: string, meta?: string): string;
|
|
19
19
|
/** Convenience: print a single status line. */
|
|
20
20
|
export declare function printStatus(kind: StatusKind, text: string, meta?: string): void;
|
|
21
|
-
export { MASTER_UNREACHABLE_MESSAGE, formatMemoriesWaitingToSync, formatFailedToSyncCount, formatOfflinePushStarting, renderClientSyncStatusLines, type ClientSyncStatusInput, } from "../remoteRender.js";
|
|
@@ -46,5 +46,3 @@ export function Status(kind, text, meta) {
|
|
|
46
46
|
export function printStatus(kind, text, meta) {
|
|
47
47
|
process.stdout.write(`${Status(kind, text, meta)}\n`);
|
|
48
48
|
}
|
|
49
|
-
// ─── v13 multi-machine sync (re-export render helpers for CLI/MCP callers) ─
|
|
50
|
-
export { MASTER_UNREACHABLE_MESSAGE, formatMemoriesWaitingToSync, formatFailedToSyncCount, formatOfflinePushStarting, renderClientSyncStatusLines, } from "../remoteRender.js";
|
package/dist/lib/setup.d.ts
CHANGED
|
@@ -138,20 +138,6 @@ export declare function runSetup(opts: {
|
|
|
138
138
|
directory?: string;
|
|
139
139
|
nonInteractive?: boolean;
|
|
140
140
|
}): Promise<SetupResult>;
|
|
141
|
-
export interface ProviderOnlySetupOpts {
|
|
142
|
-
directory?: string;
|
|
143
|
-
rl?: ReadlineInterface;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Update ONLY `llm.defaultProvider` in gnosys.json. Used by the summary
|
|
147
|
-
* panel row 1 ("provider") so it stops dragging the user into the full
|
|
148
|
-
* model picker — that's row 2's job.
|
|
149
|
-
*
|
|
150
|
-
* v5.9.4 Bug 4 — before this split, both summary rows routed through
|
|
151
|
-
* `runModelsSetup`, leaving no way to swap provider without also choosing
|
|
152
|
-
* a new model. Now row 1 picks a provider, row 2 picks a model.
|
|
153
|
-
*/
|
|
154
|
-
export declare function runProviderOnlySetup(opts?: ProviderOnlySetupOpts): Promise<void>;
|
|
155
141
|
/** Where validated keys are persisted (§3.10). */
|
|
156
142
|
export type KeyPersistDestination = "secure" | "dotenv" | "none";
|
|
157
143
|
/**
|
|
@@ -252,4 +238,3 @@ export declare function getApiKeyForProvider(provider: string, opts?: {
|
|
|
252
238
|
task?: LlmTaskName;
|
|
253
239
|
directory?: string;
|
|
254
240
|
}): Promise<string>;
|
|
255
|
-
export { getApiKeyForProviderFromConfig } from "./apiKeyVault.js";
|