@locusai/cli 0.14.2 → 0.14.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/bin/locus.js +2096 -1835
- package/package.json +3 -3
package/bin/locus.js
CHANGED
|
@@ -41904,133 +41904,446 @@ var init_index_node = __esm(() => {
|
|
|
41904
41904
|
init_planning();
|
|
41905
41905
|
});
|
|
41906
41906
|
|
|
41907
|
-
// src/
|
|
41908
|
-
import
|
|
41909
|
-
|
|
41910
|
-
|
|
41911
|
-
|
|
41912
|
-
|
|
41913
|
-
|
|
41914
|
-
|
|
41915
|
-
|
|
41916
|
-
|
|
41917
|
-
|
|
41918
|
-
|
|
41919
|
-
|
|
41920
|
-
|
|
41921
|
-
|
|
41922
|
-
break;
|
|
41923
|
-
case "Bash":
|
|
41924
|
-
lines.push(...this.formatBashTool(tool));
|
|
41925
|
-
break;
|
|
41926
|
-
case "Grep":
|
|
41927
|
-
lines.push(...this.formatGrepTool(tool));
|
|
41928
|
-
break;
|
|
41929
|
-
case "Glob":
|
|
41930
|
-
lines.push(...this.formatGlobTool(tool));
|
|
41931
|
-
break;
|
|
41932
|
-
case "WebFetch":
|
|
41933
|
-
lines.push(...this.formatWebFetchTool(tool));
|
|
41934
|
-
break;
|
|
41935
|
-
case "Task":
|
|
41936
|
-
lines.push(...this.formatTaskTool(tool));
|
|
41937
|
-
break;
|
|
41938
|
-
default:
|
|
41939
|
-
lines.push(...this.formatGenericTool(tool));
|
|
41907
|
+
// src/utils/version.ts
|
|
41908
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
41909
|
+
import { dirname as dirname3, join as join12 } from "node:path";
|
|
41910
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
41911
|
+
function getVersion() {
|
|
41912
|
+
try {
|
|
41913
|
+
const __filename2 = fileURLToPath3(import.meta.url);
|
|
41914
|
+
const __dirname2 = dirname3(__filename2);
|
|
41915
|
+
const bundledPath = join12(__dirname2, "..", "package.json");
|
|
41916
|
+
const sourcePath = join12(__dirname2, "..", "..", "package.json");
|
|
41917
|
+
if (existsSync12(bundledPath)) {
|
|
41918
|
+
const pkg = JSON.parse(readFileSync11(bundledPath, "utf-8"));
|
|
41919
|
+
if (pkg.name === "@locusai/cli") {
|
|
41920
|
+
return pkg.version || "0.0.0";
|
|
41921
|
+
}
|
|
41940
41922
|
}
|
|
41941
|
-
|
|
41942
|
-
|
|
41943
|
-
|
|
41944
|
-
|
|
41945
|
-
if (result.success) {
|
|
41946
|
-
const durationStr = result.duration ? ` (${result.duration}ms)` : "";
|
|
41947
|
-
lines.push(c.green(` ✓ Completed${durationStr}`));
|
|
41948
|
-
const resultDetails = this.formatResultDetails(tool.name, result.data);
|
|
41949
|
-
if (resultDetails) {
|
|
41950
|
-
lines.push(c.dim(` ${resultDetails}`));
|
|
41923
|
+
if (existsSync12(sourcePath)) {
|
|
41924
|
+
const pkg = JSON.parse(readFileSync11(sourcePath, "utf-8"));
|
|
41925
|
+
if (pkg.name === "@locusai/cli") {
|
|
41926
|
+
return pkg.version || "0.0.0";
|
|
41951
41927
|
}
|
|
41952
|
-
} else {
|
|
41953
|
-
const errorMsg = result.error ? this.truncate(result.error, 60) : "Unknown error";
|
|
41954
|
-
lines.push(c.red(` ✗ Failed: ${errorMsg}`));
|
|
41955
41928
|
}
|
|
41956
|
-
|
|
41929
|
+
} catch {}
|
|
41930
|
+
return "0.0.0";
|
|
41931
|
+
}
|
|
41932
|
+
var VERSION2;
|
|
41933
|
+
var init_version = __esm(() => {
|
|
41934
|
+
VERSION2 = getVersion();
|
|
41935
|
+
});
|
|
41936
|
+
|
|
41937
|
+
// src/utils/banner.ts
|
|
41938
|
+
function printBanner() {
|
|
41939
|
+
console.log(c.primary(`
|
|
41940
|
+
_ ____ ____ _ _ ____
|
|
41941
|
+
| | / __ \\ / ___| | | |/ ___|
|
|
41942
|
+
| | | | | | | | | | |\\___ \\
|
|
41943
|
+
| |___| |__| | |___| |_| |___) |
|
|
41944
|
+
|_____|\\____/ \\____|\\___/|____/ ${c.dim(`v${VERSION2}`)}
|
|
41945
|
+
`));
|
|
41946
|
+
}
|
|
41947
|
+
var init_banner = __esm(() => {
|
|
41948
|
+
init_index_node();
|
|
41949
|
+
init_version();
|
|
41950
|
+
});
|
|
41951
|
+
|
|
41952
|
+
// src/utils/helpers.ts
|
|
41953
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
41954
|
+
import { join as join13 } from "node:path";
|
|
41955
|
+
function isProjectInitialized(projectPath) {
|
|
41956
|
+
const locusDir = join13(projectPath, LOCUS_CONFIG.dir);
|
|
41957
|
+
const configPath = join13(locusDir, LOCUS_CONFIG.configFile);
|
|
41958
|
+
return existsSync13(locusDir) && existsSync13(configPath);
|
|
41959
|
+
}
|
|
41960
|
+
function requireInitialization(projectPath, command) {
|
|
41961
|
+
if (!isProjectInitialized(projectPath)) {
|
|
41962
|
+
console.error(`
|
|
41963
|
+
${c.error("✖ Error")} ${c.red(`Locus is not initialized in this directory.`)}
|
|
41964
|
+
|
|
41965
|
+
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
41966
|
+
|
|
41967
|
+
To initialize Locus in this directory, run:
|
|
41968
|
+
${c.primary("locus init")}
|
|
41969
|
+
|
|
41970
|
+
This will create a ${c.dim(".locus")} directory with the necessary configuration.
|
|
41971
|
+
`);
|
|
41972
|
+
process.exit(1);
|
|
41957
41973
|
}
|
|
41958
|
-
|
|
41959
|
-
|
|
41960
|
-
|
|
41961
|
-
|
|
41962
|
-
|
|
41963
|
-
|
|
41964
|
-
|
|
41965
|
-
|
|
41966
|
-
|
|
41967
|
-
|
|
41968
|
-
|
|
41969
|
-
|
|
41970
|
-
|
|
41971
|
-
|
|
41972
|
-
|
|
41974
|
+
}
|
|
41975
|
+
function resolveProvider3(input) {
|
|
41976
|
+
if (!input)
|
|
41977
|
+
return PROVIDER.CLAUDE;
|
|
41978
|
+
if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
|
|
41979
|
+
return input;
|
|
41980
|
+
console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
|
|
41981
|
+
process.exit(1);
|
|
41982
|
+
}
|
|
41983
|
+
var init_helpers2 = __esm(() => {
|
|
41984
|
+
init_index_node();
|
|
41985
|
+
});
|
|
41986
|
+
|
|
41987
|
+
// src/utils/index.ts
|
|
41988
|
+
var init_utils3 = __esm(() => {
|
|
41989
|
+
init_banner();
|
|
41990
|
+
init_helpers2();
|
|
41991
|
+
init_version();
|
|
41992
|
+
});
|
|
41993
|
+
|
|
41994
|
+
// src/display/execution-stats.ts
|
|
41995
|
+
class ExecutionStatsTracker {
|
|
41996
|
+
startTime;
|
|
41997
|
+
endTime = null;
|
|
41998
|
+
toolTimings = new Map;
|
|
41999
|
+
toolOrder = [];
|
|
42000
|
+
tokensUsed = null;
|
|
42001
|
+
error = null;
|
|
42002
|
+
constructor() {
|
|
42003
|
+
this.startTime = Date.now();
|
|
42004
|
+
}
|
|
42005
|
+
toolStarted(toolName, toolId) {
|
|
42006
|
+
const key = toolId ?? `${toolName}-${Date.now()}`;
|
|
42007
|
+
this.toolTimings.set(key, {
|
|
42008
|
+
name: toolName,
|
|
42009
|
+
id: toolId,
|
|
42010
|
+
startTime: Date.now()
|
|
42011
|
+
});
|
|
42012
|
+
this.toolOrder.push(key);
|
|
42013
|
+
}
|
|
42014
|
+
toolCompleted(toolName, toolId) {
|
|
42015
|
+
const key = this.findToolKey(toolName, toolId);
|
|
42016
|
+
if (key) {
|
|
42017
|
+
const timing = this.toolTimings.get(key);
|
|
42018
|
+
if (timing) {
|
|
42019
|
+
timing.endTime = Date.now();
|
|
42020
|
+
timing.duration = timing.endTime - timing.startTime;
|
|
42021
|
+
timing.success = true;
|
|
41973
42022
|
}
|
|
41974
|
-
} else {
|
|
41975
|
-
lines.push(c.cyan("\uD83D\uDCD6 Reading file"));
|
|
41976
42023
|
}
|
|
41977
|
-
return lines;
|
|
41978
42024
|
}
|
|
41979
|
-
|
|
41980
|
-
const
|
|
41981
|
-
|
|
41982
|
-
|
|
41983
|
-
|
|
41984
|
-
|
|
41985
|
-
|
|
41986
|
-
|
|
41987
|
-
|
|
41988
|
-
|
|
41989
|
-
lines.push(c.cyan("✍️ Writing file"));
|
|
42025
|
+
toolFailed(toolName, error48, toolId) {
|
|
42026
|
+
const key = this.findToolKey(toolName, toolId);
|
|
42027
|
+
if (key) {
|
|
42028
|
+
const timing = this.toolTimings.get(key);
|
|
42029
|
+
if (timing) {
|
|
42030
|
+
timing.endTime = Date.now();
|
|
42031
|
+
timing.duration = timing.endTime - timing.startTime;
|
|
42032
|
+
timing.success = false;
|
|
42033
|
+
timing.error = error48;
|
|
42034
|
+
}
|
|
41990
42035
|
}
|
|
41991
|
-
return lines;
|
|
41992
42036
|
}
|
|
41993
|
-
|
|
41994
|
-
|
|
41995
|
-
|
|
41996
|
-
|
|
41997
|
-
|
|
41998
|
-
|
|
41999
|
-
|
|
42000
|
-
|
|
42001
|
-
|
|
42002
|
-
|
|
42037
|
+
setTokensUsed(tokens) {
|
|
42038
|
+
this.tokensUsed = tokens;
|
|
42039
|
+
}
|
|
42040
|
+
setError(error48) {
|
|
42041
|
+
this.error = error48;
|
|
42042
|
+
}
|
|
42043
|
+
finalize() {
|
|
42044
|
+
this.endTime = Date.now();
|
|
42045
|
+
const toolsUsed = [];
|
|
42046
|
+
const seenTools = new Set;
|
|
42047
|
+
for (const key of this.toolOrder) {
|
|
42048
|
+
const timing = this.toolTimings.get(key);
|
|
42049
|
+
if (timing && !seenTools.has(timing.name)) {
|
|
42050
|
+
seenTools.add(timing.name);
|
|
42051
|
+
toolsUsed.push(timing.name);
|
|
42003
42052
|
}
|
|
42004
|
-
} else {
|
|
42005
|
-
lines.push(c.cyan("✏️ Editing file"));
|
|
42006
42053
|
}
|
|
42007
|
-
|
|
42054
|
+
const toolTimings = this.toolOrder.map((key) => this.toolTimings.get(key)).filter((t) => t !== undefined);
|
|
42055
|
+
const stats = {
|
|
42056
|
+
duration: this.endTime - this.startTime,
|
|
42057
|
+
toolsUsed,
|
|
42058
|
+
toolTimings,
|
|
42059
|
+
success: this.error === null
|
|
42060
|
+
};
|
|
42061
|
+
if (this.tokensUsed !== null) {
|
|
42062
|
+
stats.tokensUsed = this.tokensUsed;
|
|
42063
|
+
}
|
|
42064
|
+
if (this.error !== null) {
|
|
42065
|
+
stats.error = this.error;
|
|
42066
|
+
}
|
|
42067
|
+
return stats;
|
|
42008
42068
|
}
|
|
42009
|
-
|
|
42010
|
-
|
|
42011
|
-
|
|
42012
|
-
|
|
42013
|
-
|
|
42014
|
-
|
|
42015
|
-
|
|
42016
|
-
|
|
42017
|
-
|
|
42069
|
+
getCurrentDuration() {
|
|
42070
|
+
return Date.now() - this.startTime;
|
|
42071
|
+
}
|
|
42072
|
+
findToolKey(toolName, toolId) {
|
|
42073
|
+
if (toolId && this.toolTimings.has(toolId)) {
|
|
42074
|
+
return toolId;
|
|
42075
|
+
}
|
|
42076
|
+
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42077
|
+
const key = this.toolOrder[i];
|
|
42078
|
+
const timing = this.toolTimings.get(key);
|
|
42079
|
+
if (timing && timing.name === toolName && timing.endTime === undefined) {
|
|
42080
|
+
return key;
|
|
42018
42081
|
}
|
|
42019
|
-
|
|
42020
|
-
|
|
42082
|
+
}
|
|
42083
|
+
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42084
|
+
const key = this.toolOrder[i];
|
|
42085
|
+
const timing = this.toolTimings.get(key);
|
|
42086
|
+
if (timing && timing.name === toolName) {
|
|
42087
|
+
return key;
|
|
42021
42088
|
}
|
|
42022
|
-
} else {
|
|
42023
|
-
lines.push(c.cyan("⚡ Running command"));
|
|
42024
42089
|
}
|
|
42025
|
-
return
|
|
42090
|
+
return null;
|
|
42026
42091
|
}
|
|
42027
|
-
|
|
42028
|
-
|
|
42029
|
-
|
|
42030
|
-
|
|
42031
|
-
|
|
42032
|
-
|
|
42033
|
-
|
|
42092
|
+
}
|
|
42093
|
+
|
|
42094
|
+
// src/display/json-stream-renderer.ts
|
|
42095
|
+
class JsonStreamRenderer {
|
|
42096
|
+
sessionId;
|
|
42097
|
+
command;
|
|
42098
|
+
model;
|
|
42099
|
+
provider;
|
|
42100
|
+
cwd;
|
|
42101
|
+
statsTracker;
|
|
42102
|
+
started = false;
|
|
42103
|
+
done = false;
|
|
42104
|
+
constructor(options) {
|
|
42105
|
+
this.sessionId = options.sessionId;
|
|
42106
|
+
this.command = options.command;
|
|
42107
|
+
this.model = options.model;
|
|
42108
|
+
this.provider = options.provider;
|
|
42109
|
+
this.cwd = options.cwd;
|
|
42110
|
+
this.statsTracker = new ExecutionStatsTracker;
|
|
42111
|
+
}
|
|
42112
|
+
emitStart() {
|
|
42113
|
+
if (this.started)
|
|
42114
|
+
return;
|
|
42115
|
+
this.started = true;
|
|
42116
|
+
this.emit(createCliStreamEvent(CliStreamEventType.START, this.sessionId, {
|
|
42117
|
+
command: this.command,
|
|
42118
|
+
model: this.model,
|
|
42119
|
+
provider: this.provider,
|
|
42120
|
+
cwd: this.cwd
|
|
42121
|
+
}));
|
|
42122
|
+
}
|
|
42123
|
+
handleChunk(chunk) {
|
|
42124
|
+
this.ensureStarted();
|
|
42125
|
+
switch (chunk.type) {
|
|
42126
|
+
case "text_delta":
|
|
42127
|
+
this.emit(createCliStreamEvent(CliStreamEventType.TEXT_DELTA, this.sessionId, {
|
|
42128
|
+
content: chunk.content
|
|
42129
|
+
}));
|
|
42130
|
+
break;
|
|
42131
|
+
case "thinking":
|
|
42132
|
+
this.emit(createCliStreamEvent(CliStreamEventType.THINKING, this.sessionId, {
|
|
42133
|
+
content: chunk.content
|
|
42134
|
+
}));
|
|
42135
|
+
break;
|
|
42136
|
+
case "tool_use":
|
|
42137
|
+
this.statsTracker.toolStarted(chunk.tool, chunk.id);
|
|
42138
|
+
this.emit(createCliStreamEvent(CliStreamEventType.TOOL_STARTED, this.sessionId, {
|
|
42139
|
+
tool: chunk.tool,
|
|
42140
|
+
toolId: chunk.id,
|
|
42141
|
+
parameters: chunk.parameters
|
|
42142
|
+
}));
|
|
42143
|
+
break;
|
|
42144
|
+
case "tool_result":
|
|
42145
|
+
if (chunk.success) {
|
|
42146
|
+
this.statsTracker.toolCompleted(chunk.tool, chunk.id);
|
|
42147
|
+
} else {
|
|
42148
|
+
this.statsTracker.toolFailed(chunk.tool, chunk.error ?? "Unknown error", chunk.id);
|
|
42149
|
+
}
|
|
42150
|
+
this.emit(createCliStreamEvent(CliStreamEventType.TOOL_COMPLETED, this.sessionId, {
|
|
42151
|
+
tool: chunk.tool,
|
|
42152
|
+
toolId: chunk.id,
|
|
42153
|
+
success: chunk.success,
|
|
42154
|
+
duration: chunk.duration,
|
|
42155
|
+
error: chunk.error
|
|
42156
|
+
}));
|
|
42157
|
+
break;
|
|
42158
|
+
case "tool_parameters":
|
|
42159
|
+
break;
|
|
42160
|
+
case "result":
|
|
42161
|
+
break;
|
|
42162
|
+
case "error":
|
|
42163
|
+
this.statsTracker.setError(chunk.error);
|
|
42164
|
+
this.emitError("UNKNOWN", chunk.error);
|
|
42165
|
+
break;
|
|
42166
|
+
}
|
|
42167
|
+
}
|
|
42168
|
+
emitStatus(status, message) {
|
|
42169
|
+
this.ensureStarted();
|
|
42170
|
+
this.emit(createCliStreamEvent(CliStreamEventType.STATUS, this.sessionId, {
|
|
42171
|
+
status,
|
|
42172
|
+
message
|
|
42173
|
+
}));
|
|
42174
|
+
}
|
|
42175
|
+
emitError(code, message, options) {
|
|
42176
|
+
this.ensureStarted();
|
|
42177
|
+
this.emit(createCliStreamEvent(CliStreamEventType.ERROR, this.sessionId, {
|
|
42178
|
+
error: createProtocolError(code, message, options)
|
|
42179
|
+
}));
|
|
42180
|
+
}
|
|
42181
|
+
emitDone(exitCode) {
|
|
42182
|
+
if (this.done)
|
|
42183
|
+
return;
|
|
42184
|
+
this.done = true;
|
|
42185
|
+
this.ensureStarted();
|
|
42186
|
+
const stats = this.statsTracker.finalize();
|
|
42187
|
+
this.emit(createCliStreamEvent(CliStreamEventType.DONE, this.sessionId, {
|
|
42188
|
+
exitCode,
|
|
42189
|
+
duration: stats.duration,
|
|
42190
|
+
toolsUsed: stats.toolsUsed.length > 0 ? stats.toolsUsed : undefined,
|
|
42191
|
+
tokensUsed: stats.tokensUsed,
|
|
42192
|
+
success: exitCode === 0
|
|
42193
|
+
}));
|
|
42194
|
+
}
|
|
42195
|
+
emitFatalError(code, message, options) {
|
|
42196
|
+
this.statsTracker.setError(message);
|
|
42197
|
+
this.emitError(code, message, {
|
|
42198
|
+
...options,
|
|
42199
|
+
recoverable: false
|
|
42200
|
+
});
|
|
42201
|
+
this.emitDone(1);
|
|
42202
|
+
}
|
|
42203
|
+
isDone() {
|
|
42204
|
+
return this.done;
|
|
42205
|
+
}
|
|
42206
|
+
ensureStarted() {
|
|
42207
|
+
if (!this.started) {
|
|
42208
|
+
this.emitStart();
|
|
42209
|
+
}
|
|
42210
|
+
}
|
|
42211
|
+
emit(event) {
|
|
42212
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
42213
|
+
`);
|
|
42214
|
+
}
|
|
42215
|
+
}
|
|
42216
|
+
var init_json_stream_renderer = __esm(() => {
|
|
42217
|
+
init_src();
|
|
42218
|
+
});
|
|
42219
|
+
|
|
42220
|
+
// src/display/tool-display.ts
|
|
42221
|
+
import * as path2 from "node:path";
|
|
42222
|
+
|
|
42223
|
+
class ToolDisplay {
|
|
42224
|
+
formatToolStart(tool) {
|
|
42225
|
+
const lines = [];
|
|
42226
|
+
switch (tool.name) {
|
|
42227
|
+
case "Read":
|
|
42228
|
+
lines.push(...this.formatReadTool(tool));
|
|
42229
|
+
break;
|
|
42230
|
+
case "Write":
|
|
42231
|
+
lines.push(...this.formatWriteTool(tool));
|
|
42232
|
+
break;
|
|
42233
|
+
case "Edit":
|
|
42234
|
+
lines.push(...this.formatEditTool(tool));
|
|
42235
|
+
break;
|
|
42236
|
+
case "Bash":
|
|
42237
|
+
lines.push(...this.formatBashTool(tool));
|
|
42238
|
+
break;
|
|
42239
|
+
case "Grep":
|
|
42240
|
+
lines.push(...this.formatGrepTool(tool));
|
|
42241
|
+
break;
|
|
42242
|
+
case "Glob":
|
|
42243
|
+
lines.push(...this.formatGlobTool(tool));
|
|
42244
|
+
break;
|
|
42245
|
+
case "WebFetch":
|
|
42246
|
+
lines.push(...this.formatWebFetchTool(tool));
|
|
42247
|
+
break;
|
|
42248
|
+
case "Task":
|
|
42249
|
+
lines.push(...this.formatTaskTool(tool));
|
|
42250
|
+
break;
|
|
42251
|
+
default:
|
|
42252
|
+
lines.push(...this.formatGenericTool(tool));
|
|
42253
|
+
}
|
|
42254
|
+
return lines;
|
|
42255
|
+
}
|
|
42256
|
+
formatToolResult(tool, result) {
|
|
42257
|
+
const lines = [];
|
|
42258
|
+
if (result.success) {
|
|
42259
|
+
const durationStr = result.duration ? ` (${result.duration}ms)` : "";
|
|
42260
|
+
lines.push(c.green(` ✓ Completed${durationStr}`));
|
|
42261
|
+
const resultDetails = this.formatResultDetails(tool.name, result.data);
|
|
42262
|
+
if (resultDetails) {
|
|
42263
|
+
lines.push(c.dim(` ${resultDetails}`));
|
|
42264
|
+
}
|
|
42265
|
+
} else {
|
|
42266
|
+
const errorMsg = result.error ? this.truncate(result.error, 60) : "Unknown error";
|
|
42267
|
+
lines.push(c.red(` ✗ Failed: ${errorMsg}`));
|
|
42268
|
+
}
|
|
42269
|
+
return lines;
|
|
42270
|
+
}
|
|
42271
|
+
formatReadTool(tool) {
|
|
42272
|
+
const params = tool.parameters;
|
|
42273
|
+
const lines = [];
|
|
42274
|
+
if (params?.file_path) {
|
|
42275
|
+
const fileName = path2.basename(params.file_path);
|
|
42276
|
+
const dirPath = this.formatPath(params.file_path);
|
|
42277
|
+
lines.push(c.cyan(`\uD83D\uDCD6 Reading ${c.bold(fileName)}`));
|
|
42278
|
+
lines.push(c.dim(` ${dirPath}`));
|
|
42279
|
+
if (params.offset !== undefined || params.limit !== undefined) {
|
|
42280
|
+
const rangeInfo = [];
|
|
42281
|
+
if (params.offset !== undefined)
|
|
42282
|
+
rangeInfo.push(`offset: ${params.offset}`);
|
|
42283
|
+
if (params.limit !== undefined)
|
|
42284
|
+
rangeInfo.push(`limit: ${params.limit}`);
|
|
42285
|
+
lines.push(c.dim(` (${rangeInfo.join(", ")})`));
|
|
42286
|
+
}
|
|
42287
|
+
} else {
|
|
42288
|
+
lines.push(c.cyan("\uD83D\uDCD6 Reading file"));
|
|
42289
|
+
}
|
|
42290
|
+
return lines;
|
|
42291
|
+
}
|
|
42292
|
+
formatWriteTool(tool) {
|
|
42293
|
+
const params = tool.parameters;
|
|
42294
|
+
const lines = [];
|
|
42295
|
+
if (params?.file_path) {
|
|
42296
|
+
const fileName = path2.basename(params.file_path);
|
|
42297
|
+
const dirPath = this.formatPath(params.file_path);
|
|
42298
|
+
const size = params.content?.length ?? 0;
|
|
42299
|
+
lines.push(c.cyan(`✍️ Writing ${c.bold(fileName)}`));
|
|
42300
|
+
lines.push(c.dim(` ${dirPath} (${this.formatBytes(size)})`));
|
|
42301
|
+
} else {
|
|
42302
|
+
lines.push(c.cyan("✍️ Writing file"));
|
|
42303
|
+
}
|
|
42304
|
+
return lines;
|
|
42305
|
+
}
|
|
42306
|
+
formatEditTool(tool) {
|
|
42307
|
+
const params = tool.parameters;
|
|
42308
|
+
const lines = [];
|
|
42309
|
+
if (params?.file_path) {
|
|
42310
|
+
const fileName = path2.basename(params.file_path);
|
|
42311
|
+
const dirPath = this.formatPath(params.file_path);
|
|
42312
|
+
lines.push(c.cyan(`✏️ Editing ${c.bold(fileName)}`));
|
|
42313
|
+
lines.push(c.dim(` ${dirPath}`));
|
|
42314
|
+
if (params.replace_all) {
|
|
42315
|
+
lines.push(c.dim(" (replace all occurrences)"));
|
|
42316
|
+
}
|
|
42317
|
+
} else {
|
|
42318
|
+
lines.push(c.cyan("✏️ Editing file"));
|
|
42319
|
+
}
|
|
42320
|
+
return lines;
|
|
42321
|
+
}
|
|
42322
|
+
formatBashTool(tool) {
|
|
42323
|
+
const params = tool.parameters;
|
|
42324
|
+
const lines = [];
|
|
42325
|
+
if (params) {
|
|
42326
|
+
const description = params.description || "Running command";
|
|
42327
|
+
lines.push(c.cyan(`⚡ ${description}`));
|
|
42328
|
+
if (params.command) {
|
|
42329
|
+
const truncatedCmd = this.truncate(params.command, 80);
|
|
42330
|
+
lines.push(c.dim(` $ ${truncatedCmd}`));
|
|
42331
|
+
}
|
|
42332
|
+
if (params.timeout) {
|
|
42333
|
+
lines.push(c.dim(` (timeout: ${params.timeout}ms)`));
|
|
42334
|
+
}
|
|
42335
|
+
} else {
|
|
42336
|
+
lines.push(c.cyan("⚡ Running command"));
|
|
42337
|
+
}
|
|
42338
|
+
return lines;
|
|
42339
|
+
}
|
|
42340
|
+
formatGrepTool(tool) {
|
|
42341
|
+
const params = tool.parameters;
|
|
42342
|
+
const lines = [];
|
|
42343
|
+
if (params?.pattern) {
|
|
42344
|
+
const truncatedPattern = this.truncate(params.pattern, 40);
|
|
42345
|
+
lines.push(c.cyan(`\uD83D\uDD0D Searching for "${truncatedPattern}"`));
|
|
42346
|
+
const searchScope = [];
|
|
42034
42347
|
if (params.path)
|
|
42035
42348
|
searchScope.push(`in ${this.formatPath(params.path)}`);
|
|
42036
42349
|
if (params.glob)
|
|
@@ -42494,208 +42807,318 @@ var init_progress_renderer = __esm(() => {
|
|
|
42494
42807
|
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
42495
42808
|
});
|
|
42496
42809
|
|
|
42497
|
-
// src/
|
|
42498
|
-
|
|
42499
|
-
|
|
42500
|
-
|
|
42501
|
-
|
|
42502
|
-
|
|
42503
|
-
|
|
42504
|
-
|
|
42505
|
-
|
|
42506
|
-
|
|
42507
|
-
|
|
42508
|
-
toolStarted(toolName, toolId) {
|
|
42509
|
-
const key = toolId ?? `${toolName}-${Date.now()}`;
|
|
42510
|
-
this.toolTimings.set(key, {
|
|
42511
|
-
name: toolName,
|
|
42512
|
-
id: toolId,
|
|
42513
|
-
startTime: Date.now()
|
|
42514
|
-
});
|
|
42515
|
-
this.toolOrder.push(key);
|
|
42810
|
+
// src/settings-manager.ts
|
|
42811
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
42812
|
+
import { join as join14 } from "node:path";
|
|
42813
|
+
function getSettingsPath(projectPath) {
|
|
42814
|
+
return join14(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
42815
|
+
}
|
|
42816
|
+
|
|
42817
|
+
class SettingsManager {
|
|
42818
|
+
projectPath;
|
|
42819
|
+
constructor(projectPath) {
|
|
42820
|
+
this.projectPath = projectPath;
|
|
42516
42821
|
}
|
|
42517
|
-
|
|
42518
|
-
const
|
|
42519
|
-
if (
|
|
42520
|
-
|
|
42521
|
-
if (timing) {
|
|
42522
|
-
timing.endTime = Date.now();
|
|
42523
|
-
timing.duration = timing.endTime - timing.startTime;
|
|
42524
|
-
timing.success = true;
|
|
42525
|
-
}
|
|
42822
|
+
load() {
|
|
42823
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
42824
|
+
if (!existsSync14(settingsPath)) {
|
|
42825
|
+
return {};
|
|
42526
42826
|
}
|
|
42827
|
+
return JSON.parse(readFileSync12(settingsPath, "utf-8"));
|
|
42527
42828
|
}
|
|
42528
|
-
|
|
42529
|
-
const
|
|
42530
|
-
|
|
42531
|
-
|
|
42532
|
-
|
|
42533
|
-
timing.endTime = Date.now();
|
|
42534
|
-
timing.duration = timing.endTime - timing.startTime;
|
|
42535
|
-
timing.success = false;
|
|
42536
|
-
timing.error = error48;
|
|
42537
|
-
}
|
|
42538
|
-
}
|
|
42829
|
+
save(settings) {
|
|
42830
|
+
const { $schema: _2, ...rest } = settings;
|
|
42831
|
+
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
42832
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
42833
|
+
writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
42539
42834
|
}
|
|
42540
|
-
|
|
42541
|
-
this.
|
|
42835
|
+
get(key) {
|
|
42836
|
+
return this.load()[key];
|
|
42542
42837
|
}
|
|
42543
|
-
|
|
42544
|
-
|
|
42838
|
+
set(key, value) {
|
|
42839
|
+
const settings = this.load();
|
|
42840
|
+
settings[key] = value;
|
|
42841
|
+
this.save(settings);
|
|
42545
42842
|
}
|
|
42546
|
-
|
|
42547
|
-
|
|
42548
|
-
|
|
42549
|
-
|
|
42550
|
-
for (const key of this.toolOrder) {
|
|
42551
|
-
const timing = this.toolTimings.get(key);
|
|
42552
|
-
if (timing && !seenTools.has(timing.name)) {
|
|
42553
|
-
seenTools.add(timing.name);
|
|
42554
|
-
toolsUsed.push(timing.name);
|
|
42555
|
-
}
|
|
42556
|
-
}
|
|
42557
|
-
const toolTimings = this.toolOrder.map((key) => this.toolTimings.get(key)).filter((t) => t !== undefined);
|
|
42558
|
-
const stats = {
|
|
42559
|
-
duration: this.endTime - this.startTime,
|
|
42560
|
-
toolsUsed,
|
|
42561
|
-
toolTimings,
|
|
42562
|
-
success: this.error === null
|
|
42563
|
-
};
|
|
42564
|
-
if (this.tokensUsed !== null) {
|
|
42565
|
-
stats.tokensUsed = this.tokensUsed;
|
|
42566
|
-
}
|
|
42567
|
-
if (this.error !== null) {
|
|
42568
|
-
stats.error = this.error;
|
|
42843
|
+
remove() {
|
|
42844
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
42845
|
+
if (existsSync14(settingsPath)) {
|
|
42846
|
+
unlinkSync4(settingsPath);
|
|
42569
42847
|
}
|
|
42570
|
-
return stats;
|
|
42571
|
-
}
|
|
42572
|
-
getCurrentDuration() {
|
|
42573
|
-
return Date.now() - this.startTime;
|
|
42574
42848
|
}
|
|
42575
|
-
|
|
42576
|
-
|
|
42577
|
-
return toolId;
|
|
42578
|
-
}
|
|
42579
|
-
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42580
|
-
const key = this.toolOrder[i];
|
|
42581
|
-
const timing = this.toolTimings.get(key);
|
|
42582
|
-
if (timing && timing.name === toolName && timing.endTime === undefined) {
|
|
42583
|
-
return key;
|
|
42584
|
-
}
|
|
42585
|
-
}
|
|
42586
|
-
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42587
|
-
const key = this.toolOrder[i];
|
|
42588
|
-
const timing = this.toolTimings.get(key);
|
|
42589
|
-
if (timing && timing.name === toolName) {
|
|
42590
|
-
return key;
|
|
42591
|
-
}
|
|
42592
|
-
}
|
|
42593
|
-
return null;
|
|
42849
|
+
exists() {
|
|
42850
|
+
return existsSync14(getSettingsPath(this.projectPath));
|
|
42594
42851
|
}
|
|
42595
42852
|
}
|
|
42853
|
+
var init_settings_manager = __esm(() => {
|
|
42854
|
+
init_index_node();
|
|
42855
|
+
});
|
|
42596
42856
|
|
|
42597
|
-
// src/
|
|
42598
|
-
function
|
|
42599
|
-
const
|
|
42600
|
-
|
|
42601
|
-
|
|
42602
|
-
|
|
42603
|
-
|
|
42857
|
+
// src/commands/exec-sessions.ts
|
|
42858
|
+
function formatRelativeTime(timestamp) {
|
|
42859
|
+
const now = Date.now();
|
|
42860
|
+
const diff = now - timestamp;
|
|
42861
|
+
const seconds = Math.floor(diff / 1000);
|
|
42862
|
+
const minutes = Math.floor(seconds / 60);
|
|
42863
|
+
const hours = Math.floor(minutes / 60);
|
|
42864
|
+
const days = Math.floor(hours / 24);
|
|
42865
|
+
if (days > 0) {
|
|
42866
|
+
return days === 1 ? "yesterday" : `${days} days ago`;
|
|
42604
42867
|
}
|
|
42605
|
-
|
|
42868
|
+
if (hours > 0) {
|
|
42869
|
+
return hours === 1 ? "1 hour ago" : `${hours} hours ago`;
|
|
42870
|
+
}
|
|
42871
|
+
if (minutes > 0) {
|
|
42872
|
+
return minutes === 1 ? "1 minute ago" : `${minutes} minutes ago`;
|
|
42873
|
+
}
|
|
42874
|
+
return "just now";
|
|
42606
42875
|
}
|
|
42607
|
-
function showHelp() {
|
|
42608
|
-
console.log(`
|
|
42609
|
-
${c.primary("Available Commands")}
|
|
42610
42876
|
|
|
42611
|
-
|
|
42612
|
-
|
|
42613
|
-
|
|
42614
|
-
|
|
42615
|
-
|
|
42616
|
-
|
|
42617
|
-
|
|
42618
|
-
|
|
42877
|
+
class SessionCommands {
|
|
42878
|
+
historyManager;
|
|
42879
|
+
constructor(projectPath) {
|
|
42880
|
+
this.historyManager = new HistoryManager(projectPath);
|
|
42881
|
+
}
|
|
42882
|
+
async list() {
|
|
42883
|
+
const sessions = this.historyManager.listSessions();
|
|
42884
|
+
if (sessions.length === 0) {
|
|
42885
|
+
console.log(`
|
|
42886
|
+
${c.dim("No exec sessions found.")}
|
|
42619
42887
|
`);
|
|
42620
|
-
|
|
42621
|
-
|
|
42622
|
-
const historyManager = session2.getHistoryManager();
|
|
42623
|
-
const limit = args ? parseInt(args, 10) : 10;
|
|
42624
|
-
const sessions = historyManager.listSessions({
|
|
42625
|
-
limit: Number.isNaN(limit) ? 10 : limit
|
|
42626
|
-
});
|
|
42627
|
-
if (sessions.length === 0) {
|
|
42888
|
+
return;
|
|
42889
|
+
}
|
|
42628
42890
|
console.log(`
|
|
42629
|
-
${c.
|
|
42630
|
-
`);
|
|
42631
|
-
return;
|
|
42632
|
-
}
|
|
42633
|
-
console.log(`
|
|
42634
|
-
${c.primary("Recent Sessions")}
|
|
42891
|
+
${c.primary("Recent Exec Sessions:")}
|
|
42635
42892
|
`);
|
|
42636
|
-
|
|
42637
|
-
|
|
42638
|
-
|
|
42639
|
-
|
|
42640
|
-
|
|
42641
|
-
|
|
42642
|
-
|
|
42643
|
-
|
|
42644
|
-
|
|
42645
|
-
|
|
42646
|
-
|
|
42647
|
-
console.log(
|
|
42893
|
+
for (const session2 of sessions.slice(0, 10)) {
|
|
42894
|
+
const shortId = this.getShortId(session2.id);
|
|
42895
|
+
const age = formatRelativeTime(session2.updatedAt);
|
|
42896
|
+
const msgCount = session2.messages.length;
|
|
42897
|
+
const firstUserMsg = session2.messages.find((m) => m.role === "user");
|
|
42898
|
+
const preview = firstUserMsg ? firstUserMsg.content.slice(0, 50).replace(/\n/g, " ") : "(empty session)";
|
|
42899
|
+
console.log(` ${c.cyan(shortId)} ${c.gray("-")} ${preview}${firstUserMsg && firstUserMsg.content.length > 50 ? "..." : ""}`);
|
|
42900
|
+
console.log(` ${c.dim(`${msgCount} messages • ${age}`)}`);
|
|
42901
|
+
console.log();
|
|
42902
|
+
}
|
|
42903
|
+
if (sessions.length > 10) {
|
|
42904
|
+
console.log(c.dim(` ... and ${sessions.length - 10} more sessions
|
|
42905
|
+
`));
|
|
42648
42906
|
}
|
|
42649
42907
|
}
|
|
42650
|
-
|
|
42651
|
-
|
|
42908
|
+
async show(sessionId) {
|
|
42909
|
+
if (!sessionId) {
|
|
42910
|
+
console.error(`
|
|
42911
|
+
${c.error("Error:")} Session ID is required
|
|
42652
42912
|
`);
|
|
42653
|
-
}
|
|
42654
|
-
var REPL_COMMANDS;
|
|
42655
|
-
var init_commands = __esm(() => {
|
|
42656
|
-
init_index_node();
|
|
42657
|
-
REPL_COMMANDS = [
|
|
42658
|
-
{
|
|
42659
|
-
name: "exit",
|
|
42660
|
-
aliases: ["quit", "q"],
|
|
42661
|
-
description: "Exit interactive mode",
|
|
42662
|
-
execute: (session2) => session2.shutdown()
|
|
42663
|
-
},
|
|
42664
|
-
{
|
|
42665
|
-
name: "clear",
|
|
42666
|
-
aliases: ["cls"],
|
|
42667
|
-
description: "Clear the screen",
|
|
42668
|
-
execute: () => console.clear()
|
|
42669
|
-
},
|
|
42670
|
-
{
|
|
42671
|
-
name: "help",
|
|
42672
|
-
aliases: ["?", "h"],
|
|
42673
|
-
description: "Show available commands",
|
|
42674
|
-
execute: () => showHelp()
|
|
42675
|
-
},
|
|
42676
|
-
{
|
|
42677
|
-
name: "reset",
|
|
42678
|
-
aliases: ["r"],
|
|
42679
|
-
description: "Reset conversation context",
|
|
42680
|
-
execute: (session2) => session2.resetContext()
|
|
42681
|
-
},
|
|
42682
|
-
{
|
|
42683
|
-
name: "history",
|
|
42684
|
-
aliases: ["hist"],
|
|
42685
|
-
description: "List recent sessions",
|
|
42686
|
-
execute: (session2, args) => showHistory(session2, args)
|
|
42687
|
-
},
|
|
42688
|
-
{
|
|
42689
|
-
name: "session",
|
|
42690
|
-
aliases: ["sid"],
|
|
42691
|
-
description: "Show current session ID",
|
|
42692
|
-
execute: (session2) => {
|
|
42693
|
-
console.log(`
|
|
42694
|
-
${c.dim("Session ID:")} ${c.cyan(session2.getSessionId())}
|
|
42913
|
+
console.log(` ${c.dim("Usage: locus exec sessions show <session-id>")}
|
|
42695
42914
|
`);
|
|
42696
|
-
|
|
42915
|
+
return;
|
|
42697
42916
|
}
|
|
42698
|
-
|
|
42917
|
+
const session2 = this.historyManager.findSessionByPartialId(sessionId);
|
|
42918
|
+
if (!session2) {
|
|
42919
|
+
console.error(`
|
|
42920
|
+
${c.error("Error:")} Session ${c.cyan(sessionId)} not found
|
|
42921
|
+
`);
|
|
42922
|
+
console.log(` ${c.dim("Use 'locus exec sessions list' to see available sessions")}
|
|
42923
|
+
`);
|
|
42924
|
+
return;
|
|
42925
|
+
}
|
|
42926
|
+
console.log(`
|
|
42927
|
+
${c.primary("Session:")} ${c.cyan(session2.id)}`);
|
|
42928
|
+
console.log(` ${c.dim(`Created: ${new Date(session2.createdAt).toLocaleString()}`)}`);
|
|
42929
|
+
console.log(` ${c.dim(`Model: ${session2.metadata.model} (${session2.metadata.provider})`)}
|
|
42930
|
+
`);
|
|
42931
|
+
if (session2.messages.length === 0) {
|
|
42932
|
+
console.log(` ${c.dim("(No messages in this session)")}
|
|
42933
|
+
`);
|
|
42934
|
+
return;
|
|
42935
|
+
}
|
|
42936
|
+
console.log(c.dim(" ─".repeat(30)));
|
|
42937
|
+
console.log();
|
|
42938
|
+
for (const message of session2.messages) {
|
|
42939
|
+
const role = message.role === "user" ? c.cyan("You") : c.green("AI");
|
|
42940
|
+
const content = message.content;
|
|
42941
|
+
console.log(` ${role}:`);
|
|
42942
|
+
const lines = content.split(`
|
|
42943
|
+
`);
|
|
42944
|
+
for (const line of lines) {
|
|
42945
|
+
console.log(` ${line}`);
|
|
42946
|
+
}
|
|
42947
|
+
console.log();
|
|
42948
|
+
}
|
|
42949
|
+
}
|
|
42950
|
+
async delete(sessionId) {
|
|
42951
|
+
if (!sessionId) {
|
|
42952
|
+
console.error(`
|
|
42953
|
+
${c.error("Error:")} Session ID is required
|
|
42954
|
+
`);
|
|
42955
|
+
console.log(` ${c.dim("Usage: locus exec sessions delete <session-id>")}
|
|
42956
|
+
`);
|
|
42957
|
+
return;
|
|
42958
|
+
}
|
|
42959
|
+
const session2 = this.historyManager.findSessionByPartialId(sessionId);
|
|
42960
|
+
if (!session2) {
|
|
42961
|
+
console.error(`
|
|
42962
|
+
${c.error("Error:")} Session ${c.cyan(sessionId)} not found
|
|
42963
|
+
`);
|
|
42964
|
+
return;
|
|
42965
|
+
}
|
|
42966
|
+
const deleted = this.historyManager.deleteSession(session2.id);
|
|
42967
|
+
if (deleted) {
|
|
42968
|
+
console.log(`
|
|
42969
|
+
${c.success("✔")} Deleted session ${c.cyan(this.getShortId(session2.id))}
|
|
42970
|
+
`);
|
|
42971
|
+
} else {
|
|
42972
|
+
console.error(`
|
|
42973
|
+
${c.error("Error:")} Failed to delete session
|
|
42974
|
+
`);
|
|
42975
|
+
}
|
|
42976
|
+
}
|
|
42977
|
+
async clear() {
|
|
42978
|
+
const count = this.historyManager.getSessionCount();
|
|
42979
|
+
if (count === 0) {
|
|
42980
|
+
console.log(`
|
|
42981
|
+
${c.dim("No sessions to clear.")}
|
|
42982
|
+
`);
|
|
42983
|
+
return;
|
|
42984
|
+
}
|
|
42985
|
+
const deleted = this.historyManager.clearAllSessions();
|
|
42986
|
+
console.log(`
|
|
42987
|
+
${c.success("✔")} Cleared ${deleted} exec session${deleted === 1 ? "" : "s"}
|
|
42988
|
+
`);
|
|
42989
|
+
}
|
|
42990
|
+
getShortId(sessionId) {
|
|
42991
|
+
const parts = sessionId.split("-");
|
|
42992
|
+
if (parts.length >= 3) {
|
|
42993
|
+
return parts.slice(-1)[0].slice(0, 8);
|
|
42994
|
+
}
|
|
42995
|
+
return sessionId.slice(0, 8);
|
|
42996
|
+
}
|
|
42997
|
+
}
|
|
42998
|
+
function showSessionsHelp() {
|
|
42999
|
+
console.log(`
|
|
43000
|
+
${c.primary("Session Commands")}
|
|
43001
|
+
|
|
43002
|
+
${c.success("list")} List recent exec sessions
|
|
43003
|
+
${c.success("show")} ${c.dim("<id>")} Show all messages in a session
|
|
43004
|
+
${c.success("delete")} ${c.dim("<id>")} Delete a specific session
|
|
43005
|
+
${c.success("clear")} Clear all exec sessions
|
|
43006
|
+
|
|
43007
|
+
${c.header(" EXAMPLES ")}
|
|
43008
|
+
${c.dim("$")} locus exec sessions list
|
|
43009
|
+
${c.dim("$")} locus exec sessions show e7f3a2b1
|
|
43010
|
+
${c.dim("$")} locus exec sessions delete e7f3a2b1
|
|
43011
|
+
${c.dim("$")} locus exec sessions clear
|
|
43012
|
+
|
|
43013
|
+
${c.dim("Session IDs can be partial (first 8 characters).")}
|
|
43014
|
+
`);
|
|
43015
|
+
}
|
|
43016
|
+
var init_exec_sessions = __esm(() => {
|
|
43017
|
+
init_index_node();
|
|
43018
|
+
});
|
|
43019
|
+
|
|
43020
|
+
// src/repl/commands.ts
|
|
43021
|
+
function parseCommand(input) {
|
|
43022
|
+
const lowerInput = input.toLowerCase();
|
|
43023
|
+
for (const cmd of REPL_COMMANDS) {
|
|
43024
|
+
if (lowerInput === cmd.name || cmd.aliases.includes(lowerInput)) {
|
|
43025
|
+
return cmd;
|
|
43026
|
+
}
|
|
43027
|
+
}
|
|
43028
|
+
return null;
|
|
43029
|
+
}
|
|
43030
|
+
function showHelp() {
|
|
43031
|
+
console.log(`
|
|
43032
|
+
${c.primary("Available Commands")}
|
|
43033
|
+
|
|
43034
|
+
${c.success("exit")} / ${c.dim("quit, q")} Exit interactive mode
|
|
43035
|
+
${c.success("clear")} / ${c.dim("cls")} Clear the screen
|
|
43036
|
+
${c.success("help")} / ${c.dim("?, h")} Show this help message
|
|
43037
|
+
${c.success("reset")} / ${c.dim("r")} Reset conversation context
|
|
43038
|
+
${c.success("history")} / ${c.dim("hist")} List recent sessions
|
|
43039
|
+
${c.success("session")} / ${c.dim("sid")} Show current session ID
|
|
43040
|
+
|
|
43041
|
+
${c.dim("Any other input will be sent as a prompt to the AI.")}
|
|
43042
|
+
`);
|
|
43043
|
+
}
|
|
43044
|
+
function showHistory(session2, args) {
|
|
43045
|
+
const historyManager = session2.getHistoryManager();
|
|
43046
|
+
const limit = args ? parseInt(args, 10) : 10;
|
|
43047
|
+
const sessions = historyManager.listSessions({
|
|
43048
|
+
limit: Number.isNaN(limit) ? 10 : limit
|
|
43049
|
+
});
|
|
43050
|
+
if (sessions.length === 0) {
|
|
43051
|
+
console.log(`
|
|
43052
|
+
${c.dim("No sessions found.")}
|
|
43053
|
+
`);
|
|
43054
|
+
return;
|
|
43055
|
+
}
|
|
43056
|
+
console.log(`
|
|
43057
|
+
${c.primary("Recent Sessions")}
|
|
43058
|
+
`);
|
|
43059
|
+
for (const sess of sessions) {
|
|
43060
|
+
const date5 = new Date(sess.updatedAt);
|
|
43061
|
+
const dateStr = date5.toLocaleDateString();
|
|
43062
|
+
const timeStr = date5.toLocaleTimeString();
|
|
43063
|
+
const msgCount = sess.messages.length;
|
|
43064
|
+
const isCurrent = sess.id === session2.getSessionId();
|
|
43065
|
+
const marker = isCurrent ? c.success("*") : " ";
|
|
43066
|
+
console.log(` ${marker} ${c.cyan(sess.id)} ${c.dim(`- ${dateStr} ${timeStr} (${msgCount} messages)`)}`);
|
|
43067
|
+
if (sess.messages.length > 0) {
|
|
43068
|
+
const lastMsg = sess.messages[sess.messages.length - 1];
|
|
43069
|
+
const preview = lastMsg.content.slice(0, 60).replace(/\n/g, " ");
|
|
43070
|
+
console.log(` ${c.dim(preview + (lastMsg.content.length > 60 ? "..." : ""))}`);
|
|
43071
|
+
}
|
|
43072
|
+
}
|
|
43073
|
+
console.log(`
|
|
43074
|
+
${c.dim("Use --session <id> to resume a session")}
|
|
43075
|
+
`);
|
|
43076
|
+
}
|
|
43077
|
+
var REPL_COMMANDS;
|
|
43078
|
+
var init_commands = __esm(() => {
|
|
43079
|
+
init_index_node();
|
|
43080
|
+
REPL_COMMANDS = [
|
|
43081
|
+
{
|
|
43082
|
+
name: "exit",
|
|
43083
|
+
aliases: ["quit", "q"],
|
|
43084
|
+
description: "Exit interactive mode",
|
|
43085
|
+
execute: (session2) => session2.shutdown()
|
|
43086
|
+
},
|
|
43087
|
+
{
|
|
43088
|
+
name: "clear",
|
|
43089
|
+
aliases: ["cls"],
|
|
43090
|
+
description: "Clear the screen",
|
|
43091
|
+
execute: () => console.clear()
|
|
43092
|
+
},
|
|
43093
|
+
{
|
|
43094
|
+
name: "help",
|
|
43095
|
+
aliases: ["?", "h"],
|
|
43096
|
+
description: "Show available commands",
|
|
43097
|
+
execute: () => showHelp()
|
|
43098
|
+
},
|
|
43099
|
+
{
|
|
43100
|
+
name: "reset",
|
|
43101
|
+
aliases: ["r"],
|
|
43102
|
+
description: "Reset conversation context",
|
|
43103
|
+
execute: (session2) => session2.resetContext()
|
|
43104
|
+
},
|
|
43105
|
+
{
|
|
43106
|
+
name: "history",
|
|
43107
|
+
aliases: ["hist"],
|
|
43108
|
+
description: "List recent sessions",
|
|
43109
|
+
execute: (session2, args) => showHistory(session2, args)
|
|
43110
|
+
},
|
|
43111
|
+
{
|
|
43112
|
+
name: "session",
|
|
43113
|
+
aliases: ["sid"],
|
|
43114
|
+
description: "Show current session ID",
|
|
43115
|
+
execute: (session2) => {
|
|
43116
|
+
console.log(`
|
|
43117
|
+
${c.dim("Session ID:")} ${c.cyan(session2.getSessionId())}
|
|
43118
|
+
`);
|
|
43119
|
+
}
|
|
43120
|
+
}
|
|
43121
|
+
];
|
|
42699
43122
|
});
|
|
42700
43123
|
|
|
42701
43124
|
// src/repl/interactive-session.ts
|
|
@@ -42703,7 +43126,7 @@ var exports_interactive_session = {};
|
|
|
42703
43126
|
__export(exports_interactive_session, {
|
|
42704
43127
|
InteractiveSession: () => InteractiveSession
|
|
42705
43128
|
});
|
|
42706
|
-
import * as
|
|
43129
|
+
import * as readline from "node:readline";
|
|
42707
43130
|
|
|
42708
43131
|
class InteractiveSession {
|
|
42709
43132
|
readline = null;
|
|
@@ -42749,7 +43172,7 @@ class InteractiveSession {
|
|
|
42749
43172
|
}
|
|
42750
43173
|
async start() {
|
|
42751
43174
|
this.printWelcome();
|
|
42752
|
-
this.readline =
|
|
43175
|
+
this.readline = readline.createInterface({
|
|
42753
43176
|
input: process.stdin,
|
|
42754
43177
|
output: process.stdout,
|
|
42755
43178
|
terminal: true
|
|
@@ -42937,1700 +43360,1519 @@ var init_interactive_session = __esm(() => {
|
|
|
42937
43360
|
init_commands();
|
|
42938
43361
|
});
|
|
42939
43362
|
|
|
42940
|
-
// src/
|
|
42941
|
-
|
|
42942
|
-
|
|
42943
|
-
|
|
42944
|
-
|
|
42945
|
-
import {
|
|
42946
|
-
|
|
42947
|
-
|
|
42948
|
-
|
|
42949
|
-
|
|
42950
|
-
|
|
42951
|
-
|
|
42952
|
-
|
|
42953
|
-
}
|
|
42954
|
-
|
|
42955
|
-
|
|
42956
|
-
|
|
42957
|
-
|
|
42958
|
-
|
|
42959
|
-
|
|
42960
|
-
|
|
42961
|
-
|
|
42962
|
-
|
|
42963
|
-
|
|
42964
|
-
|
|
42965
|
-
|
|
43363
|
+
// src/commands/exec.ts
|
|
43364
|
+
var exports_exec = {};
|
|
43365
|
+
__export(exports_exec, {
|
|
43366
|
+
execCommand: () => execCommand
|
|
43367
|
+
});
|
|
43368
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
43369
|
+
import { parseArgs } from "node:util";
|
|
43370
|
+
async function execCommand(args) {
|
|
43371
|
+
const { values, positionals } = parseArgs({
|
|
43372
|
+
args,
|
|
43373
|
+
options: {
|
|
43374
|
+
model: { type: "string" },
|
|
43375
|
+
provider: { type: "string" },
|
|
43376
|
+
"reasoning-effort": { type: "string" },
|
|
43377
|
+
dir: { type: "string" },
|
|
43378
|
+
"no-stream": { type: "boolean" },
|
|
43379
|
+
"no-status": { type: "boolean" },
|
|
43380
|
+
interactive: { type: "boolean", short: "i" },
|
|
43381
|
+
session: { type: "string", short: "s" },
|
|
43382
|
+
"session-id": { type: "string" },
|
|
43383
|
+
"json-stream": { type: "boolean" }
|
|
43384
|
+
},
|
|
43385
|
+
strict: false
|
|
43386
|
+
});
|
|
43387
|
+
const jsonStream = values["json-stream"];
|
|
43388
|
+
const projectPath = values.dir || process.cwd();
|
|
43389
|
+
if (jsonStream) {
|
|
43390
|
+
await execJsonStream(values, positionals, projectPath);
|
|
43391
|
+
return;
|
|
42966
43392
|
}
|
|
42967
|
-
|
|
42968
|
-
|
|
42969
|
-
const
|
|
42970
|
-
const
|
|
42971
|
-
|
|
42972
|
-
|
|
42973
|
-
|
|
42974
|
-
|
|
42975
|
-
|
|
42976
|
-
|
|
42977
|
-
|
|
42978
|
-
|
|
42979
|
-
|
|
42980
|
-
|
|
42981
|
-
|
|
42982
|
-
|
|
42983
|
-
|
|
42984
|
-
|
|
42985
|
-
|
|
42986
|
-
|
|
42987
|
-
|
|
42988
|
-
return existsSync12(getSettingsPath(this.projectPath));
|
|
42989
|
-
}
|
|
42990
|
-
}
|
|
42991
|
-
|
|
42992
|
-
// src/commands/config.ts
|
|
42993
|
-
function ask(question) {
|
|
42994
|
-
const rl = createInterface({
|
|
42995
|
-
input: process.stdin,
|
|
42996
|
-
output: process.stdout
|
|
42997
|
-
});
|
|
42998
|
-
return new Promise((resolve2) => {
|
|
42999
|
-
rl.question(question, (answer) => {
|
|
43000
|
-
rl.close();
|
|
43001
|
-
resolve2(answer.trim());
|
|
43002
|
-
});
|
|
43003
|
-
});
|
|
43004
|
-
}
|
|
43005
|
-
var TOP_LEVEL_KEYS = [
|
|
43006
|
-
"apiKey",
|
|
43007
|
-
"apiUrl",
|
|
43008
|
-
"provider",
|
|
43009
|
-
"model",
|
|
43010
|
-
"workspaceId"
|
|
43011
|
-
];
|
|
43012
|
-
var TELEGRAM_KEYS = [
|
|
43013
|
-
"telegram.botToken",
|
|
43014
|
-
"telegram.chatId",
|
|
43015
|
-
"telegram.testMode"
|
|
43016
|
-
];
|
|
43017
|
-
var ALL_KEYS = [...TOP_LEVEL_KEYS, ...TELEGRAM_KEYS];
|
|
43018
|
-
function maskSecret(value) {
|
|
43019
|
-
if (value.length <= 8)
|
|
43020
|
-
return "****";
|
|
43021
|
-
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
43022
|
-
}
|
|
43023
|
-
function showConfigHelp() {
|
|
43024
|
-
console.log(`
|
|
43025
|
-
${c.header(" CONFIG ")}
|
|
43026
|
-
${c.primary("locus config")} ${c.dim("<subcommand> [options]")}
|
|
43027
|
-
|
|
43028
|
-
${c.header(" SUBCOMMANDS ")}
|
|
43029
|
-
${c.success("setup")} Interactive configuration (or pass flags below)
|
|
43030
|
-
${c.dim("--api-key <KEY> Locus API key (required)")}
|
|
43031
|
-
${c.dim("--api-url <URL> API base URL (optional)")}
|
|
43032
|
-
${c.dim("--provider <P> AI provider (optional)")}
|
|
43033
|
-
${c.dim("--model <M> AI model (optional)")}
|
|
43034
|
-
${c.success("show")} Show current settings
|
|
43035
|
-
${c.success("set")} Set a config value
|
|
43036
|
-
${c.dim("locus config set <key> <value>")}
|
|
43037
|
-
${c.dim(`Keys: ${ALL_KEYS.join(", ")}`)}
|
|
43038
|
-
${c.success("remove")} Remove all settings
|
|
43039
|
-
|
|
43040
|
-
${c.header(" EXAMPLES ")}
|
|
43041
|
-
${c.dim("$")} ${c.primary("locus config setup")}
|
|
43042
|
-
${c.dim("$")} ${c.primary("locus config setup --api-key sk-xxx")}
|
|
43043
|
-
${c.dim("$")} ${c.primary("locus config show")}
|
|
43044
|
-
${c.dim("$")} ${c.primary("locus config set apiKey sk-new-key")}
|
|
43045
|
-
${c.dim("$")} ${c.primary("locus config set provider codex")}
|
|
43046
|
-
${c.dim("$")} ${c.primary("locus config set telegram.botToken 123:ABC")}
|
|
43047
|
-
${c.dim("$")} ${c.primary("locus config remove")}
|
|
43048
|
-
`);
|
|
43049
|
-
}
|
|
43050
|
-
async function setupCommand(args, projectPath) {
|
|
43051
|
-
let apiKey;
|
|
43052
|
-
let apiUrl;
|
|
43053
|
-
let provider;
|
|
43054
|
-
let model;
|
|
43055
|
-
for (let i = 0;i < args.length; i++) {
|
|
43056
|
-
if (args[i] === "--api-key" && args[i + 1]) {
|
|
43057
|
-
apiKey = args[++i]?.trim();
|
|
43058
|
-
} else if (args[i] === "--api-url" && args[i + 1]) {
|
|
43059
|
-
apiUrl = args[++i]?.trim();
|
|
43060
|
-
} else if (args[i] === "--provider" && args[i + 1]) {
|
|
43061
|
-
provider = args[++i]?.trim();
|
|
43062
|
-
} else if (args[i] === "--model" && args[i + 1]) {
|
|
43063
|
-
model = args[++i]?.trim();
|
|
43064
|
-
}
|
|
43065
|
-
}
|
|
43066
|
-
if (!apiKey && !apiUrl && !provider && !model) {
|
|
43067
|
-
console.log(`
|
|
43068
|
-
${c.header(" LOCUS SETUP ")}
|
|
43069
|
-
`);
|
|
43070
|
-
console.log(` ${c.dim("Configure your Locus settings. Press Enter to skip optional fields.")}
|
|
43071
|
-
`);
|
|
43072
|
-
while (!apiKey) {
|
|
43073
|
-
apiKey = await ask(` ${c.primary("API Key")} ${c.dim("(required)")}: `);
|
|
43074
|
-
if (!apiKey) {
|
|
43075
|
-
console.log(` ${c.error("✖")} API key is required. Get one from ${c.underline("Workspace Settings > API Keys")}`);
|
|
43076
|
-
}
|
|
43077
|
-
}
|
|
43078
|
-
provider = await ask(` ${c.primary("Provider")} ${c.dim("(optional, e.g. claude, codex)")}: `);
|
|
43079
|
-
model = await ask(` ${c.primary("Model")} ${c.dim("(optional, e.g. opus, sonnet)")}: `);
|
|
43080
|
-
if (!provider)
|
|
43081
|
-
provider = undefined;
|
|
43082
|
-
if (!model)
|
|
43083
|
-
model = undefined;
|
|
43084
|
-
}
|
|
43085
|
-
if (!apiKey) {
|
|
43086
|
-
console.error(`
|
|
43087
|
-
${c.error("✖")} ${c.bold("Missing --api-key flag.")}
|
|
43088
|
-
` + ` Get an API key from ${c.underline("Workspace Settings > API Keys")}
|
|
43089
|
-
`);
|
|
43090
|
-
process.exit(1);
|
|
43091
|
-
}
|
|
43092
|
-
const manager = new SettingsManager(projectPath);
|
|
43093
|
-
const existing = manager.load();
|
|
43094
|
-
const settings = {
|
|
43095
|
-
...existing,
|
|
43096
|
-
apiKey
|
|
43097
|
-
};
|
|
43098
|
-
if (apiUrl)
|
|
43099
|
-
settings.apiUrl = apiUrl;
|
|
43100
|
-
if (provider)
|
|
43101
|
-
settings.provider = provider;
|
|
43102
|
-
if (model)
|
|
43103
|
-
settings.model = model;
|
|
43104
|
-
manager.save(settings);
|
|
43105
|
-
console.log(`
|
|
43106
|
-
${c.success("✔")} ${c.bold("Settings configured successfully!")}
|
|
43107
|
-
|
|
43108
|
-
${c.bold("Saved to:")} ${c.dim(".locus/settings.json")}
|
|
43109
|
-
|
|
43110
|
-
${c.bold("Configuration:")}
|
|
43111
|
-
${c.primary("API Key:")} ${maskSecret(apiKey)}${apiUrl ? `
|
|
43112
|
-
${c.primary("API URL:")} ${apiUrl}` : ""}${provider ? `
|
|
43113
|
-
${c.primary("Provider:")} ${provider}` : ""}${model ? `
|
|
43114
|
-
${c.primary("Model:")} ${model}` : ""}
|
|
43115
|
-
|
|
43116
|
-
${c.bold("Next steps:")}
|
|
43117
|
-
Run agents: ${c.primary("locus run")}
|
|
43118
|
-
Setup Telegram: ${c.primary("locus telegram setup")}
|
|
43119
|
-
`);
|
|
43120
|
-
}
|
|
43121
|
-
function showCommand(projectPath) {
|
|
43122
|
-
const manager = new SettingsManager(projectPath);
|
|
43123
|
-
const settings = manager.load();
|
|
43124
|
-
if (Object.keys(settings).length === 0) {
|
|
43125
|
-
console.log(`
|
|
43126
|
-
${c.dim("No settings found.")}
|
|
43127
|
-
` + ` Run ${c.primary("locus config setup")} to configure.
|
|
43128
|
-
`);
|
|
43129
|
-
return;
|
|
43130
|
-
}
|
|
43131
|
-
console.log(`
|
|
43132
|
-
${c.header(" SETTINGS ")}`);
|
|
43133
|
-
console.log(` ${c.dim("File: .locus/settings.json")}
|
|
43134
|
-
`);
|
|
43135
|
-
if (settings.apiKey) {
|
|
43136
|
-
console.log(` ${c.primary("apiKey:")} ${maskSecret(settings.apiKey)}`);
|
|
43137
|
-
}
|
|
43138
|
-
if (settings.apiUrl) {
|
|
43139
|
-
console.log(` ${c.primary("apiUrl:")} ${settings.apiUrl}`);
|
|
43140
|
-
}
|
|
43141
|
-
if (settings.provider) {
|
|
43142
|
-
console.log(` ${c.primary("provider:")} ${settings.provider}`);
|
|
43143
|
-
}
|
|
43144
|
-
if (settings.model) {
|
|
43145
|
-
console.log(` ${c.primary("model:")} ${settings.model}`);
|
|
43146
|
-
}
|
|
43147
|
-
if (settings.workspaceId) {
|
|
43148
|
-
console.log(` ${c.primary("workspaceId:")} ${settings.workspaceId}`);
|
|
43149
|
-
}
|
|
43150
|
-
if (settings.telegram) {
|
|
43151
|
-
const tg = settings.telegram;
|
|
43152
|
-
console.log(`
|
|
43153
|
-
${c.header(" TELEGRAM ")}`);
|
|
43154
|
-
if (tg.botToken) {
|
|
43155
|
-
console.log(` ${c.primary("botToken:")} ${maskSecret(tg.botToken)}`);
|
|
43156
|
-
}
|
|
43157
|
-
if (tg.chatId) {
|
|
43158
|
-
console.log(` ${c.primary("chatId:")} ${tg.chatId}`);
|
|
43159
|
-
}
|
|
43160
|
-
if (tg.testMode !== undefined) {
|
|
43161
|
-
console.log(` ${c.primary("testMode:")} ${tg.testMode}`);
|
|
43162
|
-
}
|
|
43163
|
-
}
|
|
43164
|
-
console.log("");
|
|
43165
|
-
}
|
|
43166
|
-
function setCommand(args, projectPath) {
|
|
43167
|
-
const key = args[0]?.trim();
|
|
43168
|
-
const value = args.slice(1).join(" ").trim();
|
|
43169
|
-
if (!key || !value) {
|
|
43170
|
-
console.error(`
|
|
43171
|
-
${c.error("✖")} ${c.bold("Usage:")} locus config set <key> <value>
|
|
43172
|
-
` + ` ${c.dim(`Available keys: ${ALL_KEYS.join(", ")}`)}
|
|
43173
|
-
`);
|
|
43174
|
-
process.exit(1);
|
|
43175
|
-
}
|
|
43176
|
-
if (!ALL_KEYS.includes(key)) {
|
|
43177
|
-
console.error(`
|
|
43178
|
-
${c.error("✖")} ${c.bold(`Unknown key: ${key}`)}
|
|
43179
|
-
` + ` ${c.dim(`Available keys: ${ALL_KEYS.join(", ")}`)}
|
|
43180
|
-
`);
|
|
43181
|
-
process.exit(1);
|
|
43182
|
-
}
|
|
43183
|
-
const manager = new SettingsManager(projectPath);
|
|
43184
|
-
const settings = manager.load();
|
|
43185
|
-
if (key.startsWith("telegram.")) {
|
|
43186
|
-
const telegramKey = key.replace("telegram.", "");
|
|
43187
|
-
if (!settings.telegram) {
|
|
43188
|
-
settings.telegram = {};
|
|
43189
|
-
}
|
|
43190
|
-
if (telegramKey === "chatId") {
|
|
43191
|
-
const num = Number(value);
|
|
43192
|
-
if (Number.isNaN(num)) {
|
|
43193
|
-
console.error(`
|
|
43194
|
-
${c.error("✖")} ${c.bold(`${key} must be a number.`)}
|
|
43195
|
-
`);
|
|
43196
|
-
process.exit(1);
|
|
43197
|
-
}
|
|
43198
|
-
settings.telegram[telegramKey] = num;
|
|
43199
|
-
} else if (telegramKey === "testMode") {
|
|
43200
|
-
settings.telegram[telegramKey] = value === "true" || value === "1";
|
|
43201
|
-
} else {
|
|
43202
|
-
settings.telegram[telegramKey] = value;
|
|
43203
|
-
}
|
|
43204
|
-
} else {
|
|
43205
|
-
settings[key] = value;
|
|
43206
|
-
}
|
|
43207
|
-
manager.save(settings);
|
|
43208
|
-
const displayValue = key === "apiKey" || key === "telegram.botToken" ? maskSecret(value) : value;
|
|
43209
|
-
console.log(`
|
|
43210
|
-
${c.success("✔")} Set ${c.primary(key)} = ${displayValue}
|
|
43211
|
-
`);
|
|
43212
|
-
}
|
|
43213
|
-
function removeCommand(projectPath) {
|
|
43214
|
-
const manager = new SettingsManager(projectPath);
|
|
43215
|
-
if (!manager.exists()) {
|
|
43216
|
-
console.log(`
|
|
43217
|
-
${c.dim("No settings found. Nothing to remove.")}
|
|
43218
|
-
`);
|
|
43219
|
-
return;
|
|
43220
|
-
}
|
|
43221
|
-
manager.remove();
|
|
43222
|
-
console.log(`
|
|
43223
|
-
${c.success("✔")} ${c.bold("Settings removed.")}
|
|
43224
|
-
`);
|
|
43225
|
-
}
|
|
43226
|
-
async function configCommand(args) {
|
|
43227
|
-
const projectPath = process.cwd();
|
|
43228
|
-
const subcommand = args[0];
|
|
43229
|
-
const subArgs = args.slice(1);
|
|
43230
|
-
switch (subcommand) {
|
|
43231
|
-
case "setup":
|
|
43232
|
-
await setupCommand(subArgs, projectPath);
|
|
43233
|
-
break;
|
|
43234
|
-
case "show":
|
|
43235
|
-
showCommand(projectPath);
|
|
43236
|
-
break;
|
|
43237
|
-
case "set":
|
|
43238
|
-
setCommand(subArgs, projectPath);
|
|
43239
|
-
break;
|
|
43240
|
-
case "remove":
|
|
43241
|
-
removeCommand(projectPath);
|
|
43242
|
-
break;
|
|
43243
|
-
default:
|
|
43244
|
-
showConfigHelp();
|
|
43245
|
-
}
|
|
43246
|
-
}
|
|
43247
|
-
// src/commands/discuss.ts
|
|
43248
|
-
init_index_node();
|
|
43249
|
-
init_progress_renderer();
|
|
43250
|
-
import * as readline from "node:readline";
|
|
43251
|
-
import { parseArgs } from "node:util";
|
|
43252
|
-
|
|
43253
|
-
// src/utils/banner.ts
|
|
43254
|
-
init_index_node();
|
|
43255
|
-
|
|
43256
|
-
// src/utils/version.ts
|
|
43257
|
-
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
|
|
43258
|
-
import { dirname as dirname3, join as join13 } from "node:path";
|
|
43259
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43260
|
-
function getVersion() {
|
|
43261
|
-
try {
|
|
43262
|
-
const __filename2 = fileURLToPath3(import.meta.url);
|
|
43263
|
-
const __dirname2 = dirname3(__filename2);
|
|
43264
|
-
const bundledPath = join13(__dirname2, "..", "package.json");
|
|
43265
|
-
const sourcePath = join13(__dirname2, "..", "..", "package.json");
|
|
43266
|
-
if (existsSync13(bundledPath)) {
|
|
43267
|
-
const pkg = JSON.parse(readFileSync12(bundledPath, "utf-8"));
|
|
43268
|
-
if (pkg.name === "@locusai/cli") {
|
|
43269
|
-
return pkg.version || "0.0.0";
|
|
43270
|
-
}
|
|
43271
|
-
}
|
|
43272
|
-
if (existsSync13(sourcePath)) {
|
|
43273
|
-
const pkg = JSON.parse(readFileSync12(sourcePath, "utf-8"));
|
|
43274
|
-
if (pkg.name === "@locusai/cli") {
|
|
43275
|
-
return pkg.version || "0.0.0";
|
|
43276
|
-
}
|
|
43393
|
+
requireInitialization(projectPath, "exec");
|
|
43394
|
+
if (positionals[0] === "sessions") {
|
|
43395
|
+
const sessionAction = positionals[1];
|
|
43396
|
+
const sessionArg = positionals[2];
|
|
43397
|
+
const cmds = new SessionCommands(projectPath);
|
|
43398
|
+
switch (sessionAction) {
|
|
43399
|
+
case "list":
|
|
43400
|
+
await cmds.list();
|
|
43401
|
+
return;
|
|
43402
|
+
case "show":
|
|
43403
|
+
await cmds.show(sessionArg);
|
|
43404
|
+
return;
|
|
43405
|
+
case "delete":
|
|
43406
|
+
await cmds.delete(sessionArg);
|
|
43407
|
+
return;
|
|
43408
|
+
case "clear":
|
|
43409
|
+
await cmds.clear();
|
|
43410
|
+
return;
|
|
43411
|
+
default:
|
|
43412
|
+
showSessionsHelp();
|
|
43413
|
+
return;
|
|
43277
43414
|
}
|
|
43278
|
-
} catch {}
|
|
43279
|
-
return "0.0.0";
|
|
43280
|
-
}
|
|
43281
|
-
var VERSION2 = getVersion();
|
|
43282
|
-
|
|
43283
|
-
// src/utils/banner.ts
|
|
43284
|
-
function printBanner() {
|
|
43285
|
-
console.log(c.primary(`
|
|
43286
|
-
_ ____ ____ _ _ ____
|
|
43287
|
-
| | / __ \\ / ___| | | |/ ___|
|
|
43288
|
-
| | | | | | | | | | |\\___ \\
|
|
43289
|
-
| |___| |__| | |___| |_| |___) |
|
|
43290
|
-
|_____|\\____/ \\____|\\___/|____/ ${c.dim(`v${VERSION2}`)}
|
|
43291
|
-
`));
|
|
43292
|
-
}
|
|
43293
|
-
// src/utils/helpers.ts
|
|
43294
|
-
init_index_node();
|
|
43295
|
-
import { existsSync as existsSync14 } from "node:fs";
|
|
43296
|
-
import { join as join14 } from "node:path";
|
|
43297
|
-
function isProjectInitialized(projectPath) {
|
|
43298
|
-
const locusDir = join14(projectPath, LOCUS_CONFIG.dir);
|
|
43299
|
-
const configPath = join14(locusDir, LOCUS_CONFIG.configFile);
|
|
43300
|
-
return existsSync14(locusDir) && existsSync14(configPath);
|
|
43301
|
-
}
|
|
43302
|
-
function requireInitialization(projectPath, command) {
|
|
43303
|
-
if (!isProjectInitialized(projectPath)) {
|
|
43304
|
-
console.error(`
|
|
43305
|
-
${c.error("✖ Error")} ${c.red(`Locus is not initialized in this directory.`)}
|
|
43306
|
-
|
|
43307
|
-
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
43308
|
-
|
|
43309
|
-
To initialize Locus in this directory, run:
|
|
43310
|
-
${c.primary("locus init")}
|
|
43311
|
-
|
|
43312
|
-
This will create a ${c.dim(".locus")} directory with the necessary configuration.
|
|
43313
|
-
`);
|
|
43314
|
-
process.exit(1);
|
|
43315
|
-
}
|
|
43316
|
-
}
|
|
43317
|
-
function resolveProvider3(input) {
|
|
43318
|
-
if (!input)
|
|
43319
|
-
return PROVIDER.CLAUDE;
|
|
43320
|
-
if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
|
|
43321
|
-
return input;
|
|
43322
|
-
console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
|
|
43323
|
-
process.exit(1);
|
|
43324
|
-
}
|
|
43325
|
-
// src/commands/discuss.ts
|
|
43326
|
-
async function discussCommand(args) {
|
|
43327
|
-
const { values, positionals } = parseArgs({
|
|
43328
|
-
args,
|
|
43329
|
-
options: {
|
|
43330
|
-
list: { type: "boolean" },
|
|
43331
|
-
show: { type: "string" },
|
|
43332
|
-
archive: { type: "string" },
|
|
43333
|
-
delete: { type: "string" },
|
|
43334
|
-
model: { type: "string" },
|
|
43335
|
-
provider: { type: "string" },
|
|
43336
|
-
"reasoning-effort": { type: "string" },
|
|
43337
|
-
dir: { type: "string" }
|
|
43338
|
-
},
|
|
43339
|
-
strict: false,
|
|
43340
|
-
allowPositionals: true
|
|
43341
|
-
});
|
|
43342
|
-
const projectPath = values.dir || process.cwd();
|
|
43343
|
-
requireInitialization(projectPath, "discuss");
|
|
43344
|
-
const discussionManager = new DiscussionManager(projectPath);
|
|
43345
|
-
if (values.list) {
|
|
43346
|
-
return listDiscussions(discussionManager);
|
|
43347
|
-
}
|
|
43348
|
-
if (values.show) {
|
|
43349
|
-
return showDiscussion(discussionManager, values.show);
|
|
43350
|
-
}
|
|
43351
|
-
if (values.archive) {
|
|
43352
|
-
return archiveDiscussion(discussionManager, values.archive);
|
|
43353
|
-
}
|
|
43354
|
-
if (values.delete) {
|
|
43355
|
-
return deleteDiscussion(discussionManager, values.delete);
|
|
43356
43415
|
}
|
|
43357
|
-
const
|
|
43358
|
-
|
|
43359
|
-
|
|
43416
|
+
const execSettings = new SettingsManager(projectPath).load();
|
|
43417
|
+
const provider = resolveProvider3(values.provider || execSettings.provider);
|
|
43418
|
+
const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
|
|
43419
|
+
const isInteractive = values.interactive;
|
|
43420
|
+
const sessionId = values.session;
|
|
43421
|
+
if (isInteractive) {
|
|
43422
|
+
const { InteractiveSession: InteractiveSession2 } = await Promise.resolve().then(() => (init_interactive_session(), exports_interactive_session));
|
|
43423
|
+
const session2 = new InteractiveSession2({
|
|
43424
|
+
projectPath,
|
|
43425
|
+
provider,
|
|
43426
|
+
model,
|
|
43427
|
+
sessionId
|
|
43428
|
+
});
|
|
43429
|
+
await session2.start();
|
|
43360
43430
|
return;
|
|
43361
43431
|
}
|
|
43362
|
-
const
|
|
43363
|
-
|
|
43364
|
-
|
|
43432
|
+
const promptInput = positionals.join(" ");
|
|
43433
|
+
if (!promptInput) {
|
|
43434
|
+
console.error(c.error('Error: Prompt is required. Usage: locus exec "your prompt" or locus exec --interactive'));
|
|
43435
|
+
process.exit(1);
|
|
43436
|
+
}
|
|
43437
|
+
const useStreaming = !values["no-stream"];
|
|
43365
43438
|
const reasoningEffort = values["reasoning-effort"];
|
|
43366
43439
|
const aiRunner = createAiRunner(provider, {
|
|
43367
43440
|
projectPath,
|
|
43368
43441
|
model,
|
|
43369
43442
|
reasoningEffort
|
|
43370
43443
|
});
|
|
43371
|
-
const
|
|
43372
|
-
|
|
43373
|
-
|
|
43374
|
-
};
|
|
43375
|
-
|
|
43376
|
-
|
|
43377
|
-
aiRunner,
|
|
43378
|
-
discussionManager,
|
|
43379
|
-
log,
|
|
43380
|
-
provider,
|
|
43381
|
-
model
|
|
43382
|
-
});
|
|
43383
|
-
console.log(`
|
|
43384
|
-
${c.header(" DISCUSSION ")} ${c.bold("Starting interactive discussion...")}
|
|
43385
|
-
`);
|
|
43386
|
-
console.log(` ${c.dim("Topic:")} ${c.bold(topic)}`);
|
|
43387
|
-
console.log(` ${c.dim("Model:")} ${c.dim(`${model} (${provider})`)}
|
|
43388
|
-
`);
|
|
43389
|
-
const renderer = new ProgressRenderer({ animated: true });
|
|
43390
|
-
let discussionId;
|
|
43444
|
+
const builder = new PromptBuilder(projectPath);
|
|
43445
|
+
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
43446
|
+
console.log("");
|
|
43447
|
+
console.log(`${c.primary("\uD83D\uDE80")} ${c.bold("Executing prompt with repository context...")}`);
|
|
43448
|
+
console.log("");
|
|
43449
|
+
let responseContent = "";
|
|
43391
43450
|
try {
|
|
43392
|
-
|
|
43393
|
-
|
|
43394
|
-
|
|
43395
|
-
|
|
43396
|
-
|
|
43451
|
+
if (useStreaming) {
|
|
43452
|
+
const renderer = new ProgressRenderer;
|
|
43453
|
+
const statsTracker = new ExecutionStatsTracker;
|
|
43454
|
+
const stream4 = aiRunner.runStream(fullPrompt);
|
|
43455
|
+
renderer.showThinkingStarted();
|
|
43456
|
+
for await (const chunk of stream4) {
|
|
43457
|
+
switch (chunk.type) {
|
|
43458
|
+
case "text_delta":
|
|
43459
|
+
renderer.renderTextDelta(chunk.content);
|
|
43460
|
+
responseContent += chunk.content;
|
|
43461
|
+
break;
|
|
43462
|
+
case "tool_use":
|
|
43463
|
+
statsTracker.toolStarted(chunk.tool, chunk.id);
|
|
43464
|
+
renderer.showToolStarted(chunk.tool, chunk.id);
|
|
43465
|
+
break;
|
|
43466
|
+
case "thinking":
|
|
43467
|
+
renderer.showThinkingStarted();
|
|
43468
|
+
break;
|
|
43469
|
+
case "tool_result":
|
|
43470
|
+
if (chunk.success) {
|
|
43471
|
+
statsTracker.toolCompleted(chunk.tool, chunk.id);
|
|
43472
|
+
renderer.showToolCompleted(chunk.tool, undefined, chunk.id);
|
|
43473
|
+
} else {
|
|
43474
|
+
statsTracker.toolFailed(chunk.tool, chunk.error ?? "Unknown error", chunk.id);
|
|
43475
|
+
renderer.showToolFailed(chunk.tool, chunk.error ?? "Unknown error", chunk.id);
|
|
43476
|
+
}
|
|
43477
|
+
break;
|
|
43478
|
+
case "result":
|
|
43479
|
+
break;
|
|
43480
|
+
case "error": {
|
|
43481
|
+
statsTracker.setError(chunk.error);
|
|
43482
|
+
renderer.renderError(chunk.error);
|
|
43483
|
+
renderer.finalize();
|
|
43484
|
+
const errorStats = statsTracker.finalize();
|
|
43485
|
+
renderer.showSummary(errorStats);
|
|
43486
|
+
console.error(`
|
|
43487
|
+
${c.error("✖")} ${c.error("Execution failed!")}
|
|
43397
43488
|
`);
|
|
43398
|
-
|
|
43399
|
-
|
|
43400
|
-
|
|
43489
|
+
process.exit(1);
|
|
43490
|
+
}
|
|
43491
|
+
}
|
|
43492
|
+
}
|
|
43493
|
+
renderer.finalize();
|
|
43494
|
+
const stats = statsTracker.finalize();
|
|
43495
|
+
renderer.showSummary(stats);
|
|
43496
|
+
} else {
|
|
43497
|
+
responseContent = await aiRunner.run(fullPrompt);
|
|
43498
|
+
console.log(responseContent);
|
|
43499
|
+
}
|
|
43500
|
+
console.log(`
|
|
43501
|
+
${c.success("✔")} ${c.success("Execution finished!")}
|
|
43401
43502
|
`);
|
|
43402
|
-
renderer.finalize();
|
|
43403
43503
|
} catch (error48) {
|
|
43404
|
-
renderer.finalize();
|
|
43405
43504
|
console.error(`
|
|
43406
|
-
${c.error("✖")} ${c.
|
|
43505
|
+
${c.error("✖")} ${c.error("Execution failed:")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
43407
43506
|
`);
|
|
43408
43507
|
process.exit(1);
|
|
43409
43508
|
}
|
|
43410
|
-
|
|
43411
|
-
|
|
43412
|
-
const
|
|
43413
|
-
|
|
43414
|
-
|
|
43415
|
-
|
|
43509
|
+
}
|
|
43510
|
+
async function execJsonStream(values, positionals, projectPath) {
|
|
43511
|
+
const sessionId = values["session-id"] ?? values.session ?? randomUUID2();
|
|
43512
|
+
const execSettings = new SettingsManager(projectPath).load();
|
|
43513
|
+
const provider = resolveProvider3(values.provider || execSettings.provider);
|
|
43514
|
+
const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
|
|
43515
|
+
const renderer = new JsonStreamRenderer({
|
|
43516
|
+
sessionId,
|
|
43517
|
+
command: "exec",
|
|
43518
|
+
model,
|
|
43519
|
+
provider,
|
|
43520
|
+
cwd: projectPath
|
|
43416
43521
|
});
|
|
43417
|
-
|
|
43418
|
-
|
|
43419
|
-
|
|
43420
|
-
const shutdown = () => {
|
|
43421
|
-
if (isProcessing) {
|
|
43422
|
-
aiRunner.abort();
|
|
43522
|
+
const handleSignal = () => {
|
|
43523
|
+
if (renderer.isDone()) {
|
|
43524
|
+
return;
|
|
43423
43525
|
}
|
|
43424
|
-
|
|
43425
|
-
|
|
43426
|
-
|
|
43427
|
-
Goodbye!
|
|
43428
|
-
`));
|
|
43429
|
-
rl.close();
|
|
43430
|
-
process.exit(0);
|
|
43431
|
-
};
|
|
43432
|
-
process.on("SIGINT", () => {
|
|
43433
|
-
if (isProcessing) {
|
|
43434
|
-
aiRunner.abort();
|
|
43435
|
-
isProcessing = false;
|
|
43436
|
-
console.log(c.dim(`
|
|
43437
|
-
[Interrupted]`));
|
|
43438
|
-
rl.prompt();
|
|
43526
|
+
renderer.emitFatalError("PROCESS_CRASHED", "Process terminated by signal");
|
|
43527
|
+
if (process.stdout.writableNeedDrain) {
|
|
43528
|
+
process.stdout.once("drain", () => process.exit(1));
|
|
43439
43529
|
} else {
|
|
43440
|
-
|
|
43530
|
+
process.exit(1);
|
|
43441
43531
|
}
|
|
43442
|
-
}
|
|
43443
|
-
|
|
43444
|
-
|
|
43445
|
-
|
|
43446
|
-
|
|
43447
|
-
|
|
43448
|
-
|
|
43449
|
-
|
|
43450
|
-
|
|
43532
|
+
};
|
|
43533
|
+
process.on("SIGINT", handleSignal);
|
|
43534
|
+
process.on("SIGTERM", handleSignal);
|
|
43535
|
+
try {
|
|
43536
|
+
try {
|
|
43537
|
+
requireInitialization(projectPath, "exec");
|
|
43538
|
+
} catch (initError) {
|
|
43539
|
+
renderer.emitFatalError("CLI_NOT_FOUND", initError instanceof Error ? initError.message : String(initError));
|
|
43540
|
+
process.exit(1);
|
|
43451
43541
|
}
|
|
43452
|
-
const
|
|
43453
|
-
if (
|
|
43454
|
-
|
|
43455
|
-
|
|
43456
|
-
return;
|
|
43542
|
+
const promptInput = positionals.join(" ");
|
|
43543
|
+
if (!promptInput) {
|
|
43544
|
+
renderer.emitFatalError("UNKNOWN", 'Prompt is required. Usage: locus exec --json-stream "your prompt"');
|
|
43545
|
+
process.exit(1);
|
|
43457
43546
|
}
|
|
43458
|
-
|
|
43459
|
-
|
|
43460
|
-
|
|
43547
|
+
renderer.emitStart();
|
|
43548
|
+
renderer.emitStatus("running", "Building prompt context");
|
|
43549
|
+
const aiRunner = createAiRunner(provider, {
|
|
43550
|
+
projectPath,
|
|
43551
|
+
model
|
|
43552
|
+
});
|
|
43553
|
+
const builder = new PromptBuilder(projectPath);
|
|
43554
|
+
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
43555
|
+
renderer.emitStatus("streaming", "Streaming AI response");
|
|
43556
|
+
const stream4 = aiRunner.runStream(fullPrompt);
|
|
43557
|
+
for await (const chunk of stream4) {
|
|
43558
|
+
renderer.handleChunk(chunk);
|
|
43461
43559
|
}
|
|
43462
|
-
|
|
43463
|
-
|
|
43464
|
-
|
|
43465
|
-
|
|
43560
|
+
renderer.emitDone(0);
|
|
43561
|
+
process.removeListener("SIGINT", handleSignal);
|
|
43562
|
+
process.removeListener("SIGTERM", handleSignal);
|
|
43563
|
+
} catch (error48) {
|
|
43564
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
43565
|
+
if (!renderer.isDone()) {
|
|
43566
|
+
renderer.emitFatalError("UNKNOWN", message);
|
|
43466
43567
|
}
|
|
43467
|
-
|
|
43468
|
-
|
|
43469
|
-
|
|
43470
|
-
|
|
43471
|
-
|
|
43472
|
-
|
|
43473
|
-
|
|
43474
|
-
|
|
43475
|
-
|
|
43476
|
-
|
|
43477
|
-
|
|
43478
|
-
|
|
43479
|
-
|
|
43480
|
-
|
|
43481
|
-
|
|
43482
|
-
|
|
43483
|
-
|
|
43484
|
-
|
|
43485
|
-
|
|
43568
|
+
process.exit(1);
|
|
43569
|
+
}
|
|
43570
|
+
}
|
|
43571
|
+
var init_exec2 = __esm(() => {
|
|
43572
|
+
init_index_node();
|
|
43573
|
+
init_json_stream_renderer();
|
|
43574
|
+
init_progress_renderer();
|
|
43575
|
+
init_settings_manager();
|
|
43576
|
+
init_utils3();
|
|
43577
|
+
init_exec_sessions();
|
|
43578
|
+
});
|
|
43579
|
+
|
|
43580
|
+
// src/cli.ts
|
|
43581
|
+
init_index_node();
|
|
43582
|
+
|
|
43583
|
+
// src/commands/artifacts.ts
|
|
43584
|
+
init_index_node();
|
|
43585
|
+
init_utils3();
|
|
43586
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync13, statSync } from "node:fs";
|
|
43587
|
+
import { join as join15 } from "node:path";
|
|
43588
|
+
import { parseArgs as parseArgs2 } from "node:util";
|
|
43589
|
+
function listArtifacts(projectPath) {
|
|
43590
|
+
const artifactsDir = getLocusPath(projectPath, "artifactsDir");
|
|
43591
|
+
if (!existsSync15(artifactsDir)) {
|
|
43592
|
+
return [];
|
|
43593
|
+
}
|
|
43594
|
+
const files = readdirSync5(artifactsDir).filter((f) => f.endsWith(".md"));
|
|
43595
|
+
return files.map((fileName) => {
|
|
43596
|
+
const filePath = join15(artifactsDir, fileName);
|
|
43597
|
+
const stat = statSync(filePath);
|
|
43598
|
+
const name = fileName.replace(/\.md$/, "");
|
|
43599
|
+
return {
|
|
43600
|
+
name,
|
|
43601
|
+
fileName,
|
|
43602
|
+
createdAt: stat.birthtime,
|
|
43603
|
+
size: stat.size
|
|
43604
|
+
};
|
|
43605
|
+
}).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
43606
|
+
}
|
|
43607
|
+
function readArtifact(projectPath, name) {
|
|
43608
|
+
const artifactsDir = getLocusPath(projectPath, "artifactsDir");
|
|
43609
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
43610
|
+
const filePath = join15(artifactsDir, fileName);
|
|
43611
|
+
if (!existsSync15(filePath)) {
|
|
43612
|
+
return null;
|
|
43613
|
+
}
|
|
43614
|
+
const stat = statSync(filePath);
|
|
43615
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
43616
|
+
return {
|
|
43617
|
+
content,
|
|
43618
|
+
info: {
|
|
43619
|
+
name: fileName.replace(/\.md$/, ""),
|
|
43620
|
+
fileName,
|
|
43621
|
+
createdAt: stat.birthtime,
|
|
43622
|
+
size: stat.size
|
|
43623
|
+
}
|
|
43624
|
+
};
|
|
43625
|
+
}
|
|
43626
|
+
function formatSize(bytes) {
|
|
43627
|
+
if (bytes < 1024)
|
|
43628
|
+
return `${bytes}B`;
|
|
43629
|
+
const kb = bytes / 1024;
|
|
43630
|
+
if (kb < 1024)
|
|
43631
|
+
return `${kb.toFixed(1)}KB`;
|
|
43632
|
+
const mb = kb / 1024;
|
|
43633
|
+
return `${mb.toFixed(1)}MB`;
|
|
43634
|
+
}
|
|
43635
|
+
function formatDate(date5) {
|
|
43636
|
+
return date5.toLocaleDateString("en-US", {
|
|
43637
|
+
year: "numeric",
|
|
43638
|
+
month: "short",
|
|
43639
|
+
day: "numeric",
|
|
43640
|
+
hour: "2-digit",
|
|
43641
|
+
minute: "2-digit"
|
|
43642
|
+
});
|
|
43643
|
+
}
|
|
43644
|
+
async function artifactsCommand(args) {
|
|
43645
|
+
const { positionals } = parseArgs2({
|
|
43646
|
+
args,
|
|
43647
|
+
options: {
|
|
43648
|
+
dir: { type: "string" }
|
|
43649
|
+
},
|
|
43650
|
+
strict: false,
|
|
43651
|
+
allowPositionals: true
|
|
43652
|
+
});
|
|
43653
|
+
const projectPath = process.cwd();
|
|
43654
|
+
requireInitialization(projectPath, "artifacts");
|
|
43655
|
+
const subcommand = positionals[0];
|
|
43656
|
+
switch (subcommand) {
|
|
43657
|
+
case "show":
|
|
43658
|
+
case "view": {
|
|
43659
|
+
const name = positionals.slice(1).join(" ");
|
|
43660
|
+
if (!name) {
|
|
43661
|
+
console.error(`
|
|
43662
|
+
${c.error("Error:")} Artifact name is required
|
|
43486
43663
|
`);
|
|
43487
|
-
}
|
|
43488
|
-
console.log(` ${c.dim("To review:")} ${c.cyan(`locus discuss --show ${discussionId}`)}`);
|
|
43489
|
-
console.log(` ${c.dim("To list all:")} ${c.cyan("locus discuss --list")}
|
|
43664
|
+
console.log(` ${c.dim("Usage: locus artifacts show <name>")}
|
|
43490
43665
|
`);
|
|
43491
|
-
|
|
43492
|
-
|
|
43666
|
+
return;
|
|
43667
|
+
}
|
|
43668
|
+
await showArtifact(projectPath, name);
|
|
43669
|
+
break;
|
|
43670
|
+
}
|
|
43671
|
+
case "plan": {
|
|
43672
|
+
const name = positionals.slice(1).join(" ");
|
|
43673
|
+
if (!name) {
|
|
43493
43674
|
console.error(`
|
|
43494
|
-
${c.error("
|
|
43675
|
+
${c.error("Error:")} Artifact name is required
|
|
43676
|
+
`);
|
|
43677
|
+
console.log(` ${c.dim("Usage: locus artifacts plan <name>")}
|
|
43495
43678
|
`);
|
|
43679
|
+
return;
|
|
43496
43680
|
}
|
|
43497
|
-
|
|
43498
|
-
|
|
43499
|
-
return;
|
|
43681
|
+
await convertToPlan(projectPath, name);
|
|
43682
|
+
break;
|
|
43500
43683
|
}
|
|
43501
|
-
|
|
43502
|
-
|
|
43503
|
-
|
|
43504
|
-
|
|
43505
|
-
|
|
43506
|
-
|
|
43507
|
-
|
|
43508
|
-
|
|
43509
|
-
|
|
43510
|
-
|
|
43511
|
-
while (!iterResult.done) {
|
|
43512
|
-
chunkRenderer.renderChunk(iterResult.value);
|
|
43513
|
-
iterResult = await stream4.next();
|
|
43514
|
-
}
|
|
43515
|
-
result = iterResult.value;
|
|
43516
|
-
chunkRenderer.finalize();
|
|
43517
|
-
if (result.insights.length > 0) {
|
|
43518
|
-
console.log("");
|
|
43519
|
-
for (const insight of result.insights) {
|
|
43520
|
-
const tag = formatInsightTag(insight.type);
|
|
43521
|
-
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
43522
|
-
console.log(` ${c.dim(insight.content)}
|
|
43684
|
+
default:
|
|
43685
|
+
await listArtifactsCommand(projectPath);
|
|
43686
|
+
break;
|
|
43687
|
+
}
|
|
43688
|
+
}
|
|
43689
|
+
async function listArtifactsCommand(projectPath) {
|
|
43690
|
+
const artifacts = listArtifacts(projectPath);
|
|
43691
|
+
if (artifacts.length === 0) {
|
|
43692
|
+
console.log(`
|
|
43693
|
+
${c.dim("No artifacts found.")}
|
|
43523
43694
|
`);
|
|
43524
|
-
|
|
43695
|
+
return;
|
|
43696
|
+
}
|
|
43697
|
+
console.log(`
|
|
43698
|
+
${c.primary("Artifacts")} ${c.dim(`(${artifacts.length} total)`)}
|
|
43699
|
+
`);
|
|
43700
|
+
for (let i = 0;i < artifacts.length; i++) {
|
|
43701
|
+
const artifact = artifacts[i];
|
|
43702
|
+
const index = c.dim(`${String(i + 1).padStart(2)}.`);
|
|
43703
|
+
const date5 = formatDate(artifact.createdAt);
|
|
43704
|
+
const size = formatSize(artifact.size);
|
|
43705
|
+
console.log(` ${index} ${c.cyan(artifact.name)}`);
|
|
43706
|
+
console.log(` ${c.dim(`${date5} • ${size}`)}`);
|
|
43707
|
+
}
|
|
43708
|
+
console.log(`
|
|
43709
|
+
${c.dim("Use")} ${c.primary("locus artifacts show <name>")} ${c.dim("to view content")}`);
|
|
43710
|
+
console.log(` ${c.dim("Use")} ${c.primary("locus artifacts plan <name>")} ${c.dim("to convert to a plan")}
|
|
43711
|
+
`);
|
|
43712
|
+
}
|
|
43713
|
+
async function showArtifact(projectPath, name) {
|
|
43714
|
+
const result = readArtifact(projectPath, name);
|
|
43715
|
+
if (!result) {
|
|
43716
|
+
const artifacts = listArtifacts(projectPath);
|
|
43717
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
43718
|
+
if (matches.length === 1) {
|
|
43719
|
+
const match = readArtifact(projectPath, matches[0].name);
|
|
43720
|
+
if (match) {
|
|
43721
|
+
printArtifact(match.info, match.content);
|
|
43722
|
+
return;
|
|
43525
43723
|
}
|
|
43526
|
-
}
|
|
43527
|
-
|
|
43724
|
+
}
|
|
43725
|
+
if (matches.length > 1) {
|
|
43528
43726
|
console.error(`
|
|
43529
|
-
${c.error("
|
|
43727
|
+
${c.error("Error:")} Multiple artifacts match "${name}":
|
|
43530
43728
|
`);
|
|
43729
|
+
for (const m of matches) {
|
|
43730
|
+
console.log(` ${c.cyan(m.name)}`);
|
|
43731
|
+
}
|
|
43732
|
+
console.log();
|
|
43733
|
+
return;
|
|
43531
43734
|
}
|
|
43532
|
-
|
|
43533
|
-
|
|
43534
|
-
});
|
|
43535
|
-
}
|
|
43536
|
-
function listDiscussions(discussionManager) {
|
|
43537
|
-
const discussions = discussionManager.list();
|
|
43538
|
-
if (discussions.length === 0) {
|
|
43539
|
-
console.log(`
|
|
43540
|
-
${c.dim("No discussions found.")}
|
|
43735
|
+
console.error(`
|
|
43736
|
+
${c.error("Error:")} Artifact "${name}" not found
|
|
43541
43737
|
`);
|
|
43542
|
-
console.log(` ${c.dim("
|
|
43738
|
+
console.log(` ${c.dim("Use 'locus artifacts' to see available artifacts")}
|
|
43543
43739
|
`);
|
|
43544
43740
|
return;
|
|
43545
43741
|
}
|
|
43742
|
+
printArtifact(result.info, result.content);
|
|
43743
|
+
}
|
|
43744
|
+
function printArtifact(info, content) {
|
|
43745
|
+
const date5 = formatDate(info.createdAt);
|
|
43746
|
+
const size = formatSize(info.size);
|
|
43546
43747
|
console.log(`
|
|
43547
|
-
${c.
|
|
43748
|
+
${c.primary(info.name)}`);
|
|
43749
|
+
console.log(` ${c.dim(`${date5} • ${size}`)}`);
|
|
43750
|
+
console.log(c.dim(" ─".repeat(30)));
|
|
43751
|
+
console.log();
|
|
43752
|
+
console.log(content);
|
|
43753
|
+
}
|
|
43754
|
+
async function convertToPlan(projectPath, name) {
|
|
43755
|
+
const result = readArtifact(projectPath, name);
|
|
43756
|
+
if (!result) {
|
|
43757
|
+
const artifacts = listArtifacts(projectPath);
|
|
43758
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
43759
|
+
if (matches.length === 1) {
|
|
43760
|
+
const match = readArtifact(projectPath, matches[0].name);
|
|
43761
|
+
if (match) {
|
|
43762
|
+
await runPlanConversion(match.info.name);
|
|
43763
|
+
return;
|
|
43764
|
+
}
|
|
43765
|
+
}
|
|
43766
|
+
console.error(`
|
|
43767
|
+
${c.error("Error:")} Artifact "${name}" not found
|
|
43548
43768
|
`);
|
|
43549
|
-
|
|
43550
|
-
|
|
43551
|
-
|
|
43552
|
-
console.log(` ${c.dim("ID:")} ${disc.id}`);
|
|
43553
|
-
console.log(` ${c.dim("Created:")} ${disc.createdAt}`);
|
|
43554
|
-
console.log("");
|
|
43769
|
+
console.log(` ${c.dim("Use 'locus artifacts' to see available artifacts")}
|
|
43770
|
+
`);
|
|
43771
|
+
return;
|
|
43555
43772
|
}
|
|
43773
|
+
await runPlanConversion(result.info.name);
|
|
43556
43774
|
}
|
|
43557
|
-
function
|
|
43558
|
-
const
|
|
43559
|
-
|
|
43560
|
-
|
|
43561
|
-
${c.error("✖")} ${c.red(`Discussion not found: ${id}`)}
|
|
43775
|
+
async function runPlanConversion(artifactName) {
|
|
43776
|
+
const { execCommand: execCommand2 } = await Promise.resolve().then(() => (init_exec2(), exports_exec));
|
|
43777
|
+
console.log(`
|
|
43778
|
+
${c.primary("Converting artifact to plan:")} ${c.cyan(artifactName)}
|
|
43562
43779
|
`);
|
|
43563
|
-
|
|
43780
|
+
const prompt = `Create a plan according to ${artifactName}`;
|
|
43781
|
+
await execCommand2([prompt]);
|
|
43782
|
+
}
|
|
43783
|
+
// src/commands/config.ts
|
|
43784
|
+
init_index_node();
|
|
43785
|
+
init_settings_manager();
|
|
43786
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
43787
|
+
function ask(question) {
|
|
43788
|
+
const rl = createInterface2({
|
|
43789
|
+
input: process.stdin,
|
|
43790
|
+
output: process.stdout
|
|
43791
|
+
});
|
|
43792
|
+
return new Promise((resolve2) => {
|
|
43793
|
+
rl.question(question, (answer) => {
|
|
43794
|
+
rl.close();
|
|
43795
|
+
resolve2(answer.trim());
|
|
43796
|
+
});
|
|
43797
|
+
});
|
|
43798
|
+
}
|
|
43799
|
+
var TOP_LEVEL_KEYS = [
|
|
43800
|
+
"apiKey",
|
|
43801
|
+
"apiUrl",
|
|
43802
|
+
"provider",
|
|
43803
|
+
"model",
|
|
43804
|
+
"workspaceId"
|
|
43805
|
+
];
|
|
43806
|
+
var TELEGRAM_KEYS = [
|
|
43807
|
+
"telegram.botToken",
|
|
43808
|
+
"telegram.chatId",
|
|
43809
|
+
"telegram.testMode"
|
|
43810
|
+
];
|
|
43811
|
+
var ALL_KEYS = [...TOP_LEVEL_KEYS, ...TELEGRAM_KEYS];
|
|
43812
|
+
function maskSecret(value) {
|
|
43813
|
+
if (value.length <= 8)
|
|
43814
|
+
return "****";
|
|
43815
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
43816
|
+
}
|
|
43817
|
+
function showConfigHelp() {
|
|
43818
|
+
console.log(`
|
|
43819
|
+
${c.header(" CONFIG ")}
|
|
43820
|
+
${c.primary("locus config")} ${c.dim("<subcommand> [options]")}
|
|
43821
|
+
|
|
43822
|
+
${c.header(" SUBCOMMANDS ")}
|
|
43823
|
+
${c.success("setup")} Interactive configuration (or pass flags below)
|
|
43824
|
+
${c.dim("--api-key <KEY> Locus API key (required)")}
|
|
43825
|
+
${c.dim("--api-url <URL> API base URL (optional)")}
|
|
43826
|
+
${c.dim("--provider <P> AI provider (optional)")}
|
|
43827
|
+
${c.dim("--model <M> AI model (optional)")}
|
|
43828
|
+
${c.success("show")} Show current settings
|
|
43829
|
+
${c.success("set")} Set a config value
|
|
43830
|
+
${c.dim("locus config set <key> <value>")}
|
|
43831
|
+
${c.dim(`Keys: ${ALL_KEYS.join(", ")}`)}
|
|
43832
|
+
${c.success("remove")} Remove all settings
|
|
43833
|
+
|
|
43834
|
+
${c.header(" EXAMPLES ")}
|
|
43835
|
+
${c.dim("$")} ${c.primary("locus config setup")}
|
|
43836
|
+
${c.dim("$")} ${c.primary("locus config setup --api-key sk-xxx")}
|
|
43837
|
+
${c.dim("$")} ${c.primary("locus config show")}
|
|
43838
|
+
${c.dim("$")} ${c.primary("locus config set apiKey sk-new-key")}
|
|
43839
|
+
${c.dim("$")} ${c.primary("locus config set provider codex")}
|
|
43840
|
+
${c.dim("$")} ${c.primary("locus config set telegram.botToken 123:ABC")}
|
|
43841
|
+
${c.dim("$")} ${c.primary("locus config remove")}
|
|
43842
|
+
`);
|
|
43843
|
+
}
|
|
43844
|
+
async function setupCommand(args, projectPath) {
|
|
43845
|
+
let apiKey;
|
|
43846
|
+
let apiUrl;
|
|
43847
|
+
let provider;
|
|
43848
|
+
let model;
|
|
43849
|
+
for (let i = 0;i < args.length; i++) {
|
|
43850
|
+
if (args[i] === "--api-key" && args[i + 1]) {
|
|
43851
|
+
apiKey = args[++i]?.trim();
|
|
43852
|
+
} else if (args[i] === "--api-url" && args[i + 1]) {
|
|
43853
|
+
apiUrl = args[++i]?.trim();
|
|
43854
|
+
} else if (args[i] === "--provider" && args[i + 1]) {
|
|
43855
|
+
provider = args[++i]?.trim();
|
|
43856
|
+
} else if (args[i] === "--model" && args[i + 1]) {
|
|
43857
|
+
model = args[++i]?.trim();
|
|
43858
|
+
}
|
|
43564
43859
|
}
|
|
43565
|
-
|
|
43566
|
-
${md}
|
|
43567
|
-
`);
|
|
43568
|
-
}
|
|
43569
|
-
function archiveDiscussion(discussionManager, id) {
|
|
43570
|
-
try {
|
|
43571
|
-
discussionManager.archive(id);
|
|
43860
|
+
if (!apiKey && !apiUrl && !provider && !model) {
|
|
43572
43861
|
console.log(`
|
|
43573
|
-
${c.
|
|
43862
|
+
${c.header(" LOCUS SETUP ")}
|
|
43574
43863
|
`);
|
|
43575
|
-
|
|
43576
|
-
console.error(`
|
|
43577
|
-
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
43864
|
+
console.log(` ${c.dim("Configure your Locus settings. Press Enter to skip optional fields.")}
|
|
43578
43865
|
`);
|
|
43579
|
-
|
|
43866
|
+
while (!apiKey) {
|
|
43867
|
+
apiKey = await ask(` ${c.primary("API Key")} ${c.dim("(required)")}: `);
|
|
43868
|
+
if (!apiKey) {
|
|
43869
|
+
console.log(` ${c.error("✖")} API key is required. Get one from ${c.underline("Workspace Settings > API Keys")}`);
|
|
43870
|
+
}
|
|
43871
|
+
}
|
|
43872
|
+
provider = await ask(` ${c.primary("Provider")} ${c.dim("(optional, e.g. claude, codex)")}: `);
|
|
43873
|
+
model = await ask(` ${c.primary("Model")} ${c.dim("(optional, e.g. opus, sonnet)")}: `);
|
|
43874
|
+
if (!provider)
|
|
43875
|
+
provider = undefined;
|
|
43876
|
+
if (!model)
|
|
43877
|
+
model = undefined;
|
|
43580
43878
|
}
|
|
43581
|
-
|
|
43582
|
-
function deleteDiscussion(discussionManager, id) {
|
|
43583
|
-
try {
|
|
43584
|
-
discussionManager.delete(id);
|
|
43585
|
-
console.log(`
|
|
43586
|
-
${c.success("✔")} ${c.dim("Discussion deleted.")}
|
|
43587
|
-
`);
|
|
43588
|
-
} catch (error48) {
|
|
43879
|
+
if (!apiKey) {
|
|
43589
43880
|
console.error(`
|
|
43590
|
-
${c.error("✖")} ${c.
|
|
43881
|
+
${c.error("✖")} ${c.bold("Missing --api-key flag.")}
|
|
43882
|
+
` + ` Get an API key from ${c.underline("Workspace Settings > API Keys")}
|
|
43591
43883
|
`);
|
|
43592
43884
|
process.exit(1);
|
|
43593
43885
|
}
|
|
43594
|
-
|
|
43595
|
-
|
|
43596
|
-
const
|
|
43597
|
-
|
|
43598
|
-
|
|
43599
|
-
|
|
43600
|
-
|
|
43601
|
-
|
|
43602
|
-
|
|
43603
|
-
|
|
43604
|
-
|
|
43605
|
-
|
|
43606
|
-
|
|
43607
|
-
const tag = formatInsightTag(insight.type);
|
|
43608
|
-
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
43609
|
-
console.log(` ${c.dim(insight.content)}`);
|
|
43610
|
-
if (insight.tags.length > 0) {
|
|
43611
|
-
console.log(` ${c.dim(`Tags: ${insight.tags.join(", ")}`)}`);
|
|
43612
|
-
}
|
|
43613
|
-
console.log("");
|
|
43614
|
-
}
|
|
43615
|
-
}
|
|
43616
|
-
function formatInsightTag(type) {
|
|
43617
|
-
switch (type) {
|
|
43618
|
-
case "decision":
|
|
43619
|
-
return c.green("[DECISION]");
|
|
43620
|
-
case "requirement":
|
|
43621
|
-
return c.blue("[REQUIREMENT]");
|
|
43622
|
-
case "idea":
|
|
43623
|
-
return c.yellow("[IDEA]");
|
|
43624
|
-
case "concern":
|
|
43625
|
-
return c.red("[CONCERN]");
|
|
43626
|
-
case "learning":
|
|
43627
|
-
return c.cyan("[LEARNING]");
|
|
43628
|
-
}
|
|
43629
|
-
}
|
|
43630
|
-
function showReplHelp() {
|
|
43631
|
-
console.log(`
|
|
43632
|
-
${c.header(" DISCUSSION COMMANDS ")}
|
|
43633
|
-
|
|
43634
|
-
${c.cyan("summary")} Generate a final summary and end the discussion
|
|
43635
|
-
${c.cyan("insights")} Show all insights extracted so far
|
|
43636
|
-
${c.cyan("exit")} Save and exit without generating a summary
|
|
43637
|
-
${c.cyan("help")} Show this help message
|
|
43638
|
-
|
|
43639
|
-
${c.dim("Type anything else to continue the discussion.")}
|
|
43640
|
-
`);
|
|
43641
|
-
}
|
|
43642
|
-
function showDiscussHelp() {
|
|
43886
|
+
const manager = new SettingsManager(projectPath);
|
|
43887
|
+
const existing = manager.load();
|
|
43888
|
+
const settings = {
|
|
43889
|
+
...existing,
|
|
43890
|
+
apiKey
|
|
43891
|
+
};
|
|
43892
|
+
if (apiUrl)
|
|
43893
|
+
settings.apiUrl = apiUrl;
|
|
43894
|
+
if (provider)
|
|
43895
|
+
settings.provider = provider;
|
|
43896
|
+
if (model)
|
|
43897
|
+
settings.model = model;
|
|
43898
|
+
manager.save(settings);
|
|
43643
43899
|
console.log(`
|
|
43644
|
-
${c.
|
|
43645
|
-
|
|
43646
|
-
${c.bold("Usage:")}
|
|
43647
|
-
${c.cyan('locus discuss "topic"')} Start a discussion on a topic
|
|
43648
|
-
${c.cyan("locus discuss --list")} List all discussions
|
|
43649
|
-
${c.cyan("locus discuss --show <id>")} Show discussion details
|
|
43650
|
-
${c.cyan("locus discuss --archive <id>")} Archive a discussion
|
|
43651
|
-
${c.cyan("locus discuss --delete <id>")} Delete a discussion
|
|
43652
|
-
|
|
43653
|
-
${c.bold("Options:")}
|
|
43654
|
-
${c.dim("--model <model>")} AI model (claude: opus, sonnet, haiku | codex: gpt-5.3-codex, gpt-5-codex-mini)
|
|
43655
|
-
${c.dim("--provider <p>")} AI provider (claude, codex)
|
|
43656
|
-
${c.dim("--reasoning-effort <level>")} Reasoning effort (low, medium, high)
|
|
43657
|
-
${c.dim("--dir <path>")} Project directory
|
|
43658
|
-
|
|
43659
|
-
${c.bold("REPL Commands:")}
|
|
43660
|
-
${c.dim("summary")} Generate final summary and end the discussion
|
|
43661
|
-
${c.dim("insights")} Show all insights extracted so far
|
|
43662
|
-
${c.dim("exit")} Save and exit without generating a summary
|
|
43663
|
-
${c.dim("help")} Show available commands
|
|
43664
|
-
|
|
43665
|
-
${c.bold("Examples:")}
|
|
43666
|
-
${c.dim("# Start a discussion about architecture")}
|
|
43667
|
-
${c.cyan('locus discuss "how should we structure the auth system?"')}
|
|
43668
|
-
|
|
43669
|
-
${c.dim("# Review a past discussion")}
|
|
43670
|
-
${c.cyan("locus discuss --show disc-1234567890")}
|
|
43671
|
-
|
|
43672
|
-
${c.dim("# List all discussions")}
|
|
43673
|
-
${c.cyan("locus discuss --list")}
|
|
43674
|
-
`);
|
|
43675
|
-
}
|
|
43676
|
-
// src/commands/docs.ts
|
|
43677
|
-
init_index_node();
|
|
43678
|
-
import { parseArgs as parseArgs2 } from "node:util";
|
|
43679
|
-
|
|
43680
|
-
// src/config-manager.ts
|
|
43681
|
-
init_index_node();
|
|
43682
|
-
import { execSync as execSync2 } from "node:child_process";
|
|
43683
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "node:fs";
|
|
43684
|
-
import { join as join15 } from "node:path";
|
|
43685
|
-
var LOCUS_GITIGNORE_MARKER = "# Locus AI";
|
|
43686
|
-
var LOCUS_MD_TEMPLATE = `## Planning First
|
|
43687
|
-
|
|
43688
|
-
Complex tasks must be planned before writing code. Create ".locus/plans/<task-name>.md" with:
|
|
43689
|
-
- **Goal**: What we're trying to achieve and why
|
|
43690
|
-
- **Approach**: Step-by-step strategy with technical decisions
|
|
43691
|
-
- **Affected files**: List of files to create/modify/delete
|
|
43692
|
-
- **Acceptance criteria**: Specific, testable conditions for completion
|
|
43693
|
-
- **Dependencies**: Required packages, APIs, or external services
|
|
43694
|
-
|
|
43695
|
-
Delete the planning .md files after successful execution.
|
|
43696
|
-
|
|
43697
|
-
## Code Quality
|
|
43698
|
-
|
|
43699
|
-
- **Follow existing patterns**: Run formatters and linters before finishing (check "package.json" scripts or project config)
|
|
43700
|
-
- **Minimize changes**: Keep modifications atomic. Separate refactors from behavioral changes into different tasks
|
|
43701
|
-
- **Never commit secrets**: No API keys, passwords, or credentials in code. Use environment variables or secret management
|
|
43702
|
-
- **Test as you go**: If tests exist, run relevant ones. If breaking changes occur, update tests accordingly
|
|
43703
|
-
- **Comment complex logic**: Explain *why*, not *what*. Focus on business logic and non-obvious decisions
|
|
43704
|
-
|
|
43705
|
-
## Artifacts
|
|
43706
|
-
|
|
43707
|
-
When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save results as Markdown in ".locus/artifacts/<descriptive-name>.md":
|
|
43708
|
-
|
|
43709
|
-
**Always create artifacts for:**
|
|
43710
|
-
- Code quality audits, security reviews, vulnerability assessments
|
|
43711
|
-
- Architecture analyses, system design proposals, or recommendations
|
|
43712
|
-
- Dependency reports, performance profiling, benchmarking results
|
|
43713
|
-
- Research summaries, technology comparisons, or feasibility studies
|
|
43714
|
-
- Migration plans, deployment strategies, or rollback procedures
|
|
43715
|
-
- Post-mortems, incident analysis, or debugging investigations
|
|
43716
|
-
|
|
43717
|
-
**Artifact structure:**
|
|
43718
|
-
- Clear title and date
|
|
43719
|
-
- Executive summary (2-3 sentences)
|
|
43720
|
-
- Detailed findings/analysis
|
|
43721
|
-
- Actionable recommendations (if applicable)
|
|
43722
|
-
|
|
43723
|
-
## Git Operations
|
|
43724
|
-
|
|
43725
|
-
- **Do NOT run**: git add, git commit, git push, git checkout, git branch, or any git commands
|
|
43726
|
-
- **Why**: The Locus orchestrator handles all version control automatically after execution
|
|
43727
|
-
- **Your role**: Focus solely on making file changes. The system commits, pushes, and creates PRs
|
|
43728
|
-
|
|
43729
|
-
## Continuous Learning
|
|
43730
|
-
|
|
43731
|
-
Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
|
|
43732
|
-
|
|
43733
|
-
**When to update:**
|
|
43734
|
-
- User corrects your approach or provides guidance
|
|
43735
|
-
- You discover a better pattern while working
|
|
43736
|
-
- A decision prevents future confusion (e.g., "use X not Y because Z")
|
|
43737
|
-
- You encounter and solve a tricky problem
|
|
43738
|
-
|
|
43739
|
-
**What to record:**
|
|
43740
|
-
- Architectural decisions and their rationale
|
|
43741
|
-
- Preferred libraries, tools, or patterns for this project
|
|
43742
|
-
- Common pitfalls and how to avoid them
|
|
43743
|
-
- Project-specific conventions or user preferences
|
|
43744
|
-
- Solutions to non-obvious problems
|
|
43745
|
-
|
|
43746
|
-
**Format (append-only, never delete):**
|
|
43747
|
-
|
|
43748
|
-
"""
|
|
43749
|
-
- **[Category]**: Concise description (1-2 lines max). *Context if needed.*
|
|
43750
|
-
"""
|
|
43751
|
-
|
|
43752
|
-
**Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
|
|
43753
|
-
|
|
43754
|
-
## Error Handling
|
|
43755
|
-
|
|
43756
|
-
- **Read error messages carefully**: Don't guess. Parse the actual error before proposing fixes
|
|
43757
|
-
- **Check dependencies first**: Missing packages, wrong versions, and environment issues are common
|
|
43758
|
-
- **Verify assumptions**: If something "should work," confirm it actually does in this environment
|
|
43759
|
-
- **Ask for context**: If you need environment details, configuration, or logs, request them explicitly
|
|
43760
|
-
|
|
43761
|
-
## Communication
|
|
43900
|
+
${c.success("✔")} ${c.bold("Settings configured successfully!")}
|
|
43762
43901
|
|
|
43763
|
-
|
|
43764
|
-
- **Show your work**: For complex changes, briefly explain the approach before executing
|
|
43765
|
-
- **Highlight trade-offs**: If multiple approaches exist, note why you chose one over others
|
|
43766
|
-
- **Request feedback**: For ambiguous requirements, propose an approach and ask for confirmation
|
|
43767
|
-
`;
|
|
43768
|
-
var DEFAULT_LEARNINGS_MD = `# Learnings
|
|
43902
|
+
${c.bold("Saved to:")} ${c.dim(".locus/settings.json")}
|
|
43769
43903
|
|
|
43770
|
-
|
|
43771
|
-
|
|
43904
|
+
${c.bold("Configuration:")}
|
|
43905
|
+
${c.primary("API Key:")} ${maskSecret(apiKey)}${apiUrl ? `
|
|
43906
|
+
${c.primary("API URL:")} ${apiUrl}` : ""}${provider ? `
|
|
43907
|
+
${c.primary("Provider:")} ${provider}` : ""}${model ? `
|
|
43908
|
+
${c.primary("Model:")} ${model}` : ""}
|
|
43772
43909
|
|
|
43773
|
-
|
|
43774
|
-
|
|
43775
|
-
|
|
43776
|
-
const gitignorePath = join15(projectPath, ".gitignore");
|
|
43777
|
-
let content = "";
|
|
43778
|
-
const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
|
|
43779
|
-
`);
|
|
43780
|
-
if (existsSync15(gitignorePath)) {
|
|
43781
|
-
content = readFileSync13(gitignorePath, "utf-8");
|
|
43782
|
-
if (content.includes(LOCUS_GITIGNORE_MARKER)) {
|
|
43783
|
-
const lines = content.split(`
|
|
43784
|
-
`);
|
|
43785
|
-
const startIdx = lines.findIndex((l) => l.includes(LOCUS_GITIGNORE_MARKER));
|
|
43786
|
-
let endIdx = startIdx;
|
|
43787
|
-
for (let i = startIdx;i < lines.length; i++) {
|
|
43788
|
-
if (lines[i].startsWith(LOCUS_GITIGNORE_MARKER) || lines[i].startsWith(".locus") || lines[i].trim() === "") {
|
|
43789
|
-
endIdx = i;
|
|
43790
|
-
} else {
|
|
43791
|
-
break;
|
|
43792
|
-
}
|
|
43793
|
-
}
|
|
43794
|
-
const before = lines.slice(0, startIdx);
|
|
43795
|
-
const after = lines.slice(endIdx + 1);
|
|
43796
|
-
content = [...before, locusBlock, ...after].join(`
|
|
43910
|
+
${c.bold("Next steps:")}
|
|
43911
|
+
Run agents: ${c.primary("locus run")}
|
|
43912
|
+
Setup Telegram: ${c.primary("locus telegram setup")}
|
|
43797
43913
|
`);
|
|
43798
|
-
writeFileSync7(gitignorePath, content);
|
|
43799
|
-
return;
|
|
43800
|
-
}
|
|
43801
|
-
if (content.length > 0 && !content.endsWith(`
|
|
43802
|
-
`)) {
|
|
43803
|
-
content += `
|
|
43804
|
-
`;
|
|
43805
|
-
}
|
|
43806
|
-
if (content.trim().length > 0) {
|
|
43807
|
-
content += `
|
|
43808
|
-
`;
|
|
43809
|
-
}
|
|
43810
|
-
}
|
|
43811
|
-
content += `${locusBlock}
|
|
43812
|
-
`;
|
|
43813
|
-
writeFileSync7(gitignorePath, content);
|
|
43814
43914
|
}
|
|
43815
|
-
function
|
|
43816
|
-
const
|
|
43817
|
-
|
|
43818
|
-
|
|
43819
|
-
|
|
43820
|
-
|
|
43821
|
-
|
|
43822
|
-
|
|
43823
|
-
|
|
43824
|
-
}
|
|
43825
|
-
})();
|
|
43826
|
-
const hasEmail = (() => {
|
|
43827
|
-
try {
|
|
43828
|
-
return execSync2("git config --get user.email", {
|
|
43829
|
-
cwd: projectPath,
|
|
43830
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
43831
|
-
}).toString().trim();
|
|
43832
|
-
} catch {
|
|
43833
|
-
return "";
|
|
43834
|
-
}
|
|
43835
|
-
})();
|
|
43836
|
-
if (!hasName) {
|
|
43837
|
-
execSync2('git config user.name "LocusAgent"', {
|
|
43838
|
-
cwd: projectPath,
|
|
43839
|
-
stdio: "ignore"
|
|
43840
|
-
});
|
|
43915
|
+
function showCommand(projectPath) {
|
|
43916
|
+
const manager = new SettingsManager(projectPath);
|
|
43917
|
+
const settings = manager.load();
|
|
43918
|
+
if (Object.keys(settings).length === 0) {
|
|
43919
|
+
console.log(`
|
|
43920
|
+
${c.dim("No settings found.")}
|
|
43921
|
+
` + ` Run ${c.primary("locus config setup")} to configure.
|
|
43922
|
+
`);
|
|
43923
|
+
return;
|
|
43841
43924
|
}
|
|
43842
|
-
|
|
43843
|
-
|
|
43844
|
-
|
|
43845
|
-
|
|
43846
|
-
|
|
43925
|
+
console.log(`
|
|
43926
|
+
${c.header(" SETTINGS ")}`);
|
|
43927
|
+
console.log(` ${c.dim("File: .locus/settings.json")}
|
|
43928
|
+
`);
|
|
43929
|
+
if (settings.apiKey) {
|
|
43930
|
+
console.log(` ${c.primary("apiKey:")} ${maskSecret(settings.apiKey)}`);
|
|
43847
43931
|
}
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
stdio: "ignore"
|
|
43851
|
-
});
|
|
43852
|
-
}
|
|
43853
|
-
|
|
43854
|
-
class ConfigManager {
|
|
43855
|
-
projectPath;
|
|
43856
|
-
constructor(projectPath) {
|
|
43857
|
-
this.projectPath = projectPath;
|
|
43932
|
+
if (settings.apiUrl) {
|
|
43933
|
+
console.log(` ${c.primary("apiUrl:")} ${settings.apiUrl}`);
|
|
43858
43934
|
}
|
|
43859
|
-
|
|
43860
|
-
|
|
43861
|
-
const locusConfigPath = getLocusPath(this.projectPath, "configFile");
|
|
43862
|
-
if (!existsSync15(locusConfigDir)) {
|
|
43863
|
-
mkdirSync7(locusConfigDir, { recursive: true });
|
|
43864
|
-
}
|
|
43865
|
-
const locusSubdirs = [
|
|
43866
|
-
LOCUS_CONFIG.artifactsDir,
|
|
43867
|
-
LOCUS_CONFIG.documentsDir,
|
|
43868
|
-
LOCUS_CONFIG.sessionsDir,
|
|
43869
|
-
LOCUS_CONFIG.reviewsDir,
|
|
43870
|
-
LOCUS_CONFIG.plansDir,
|
|
43871
|
-
LOCUS_CONFIG.discussionsDir
|
|
43872
|
-
];
|
|
43873
|
-
for (const subdir of locusSubdirs) {
|
|
43874
|
-
const subdirPath = join15(locusConfigDir, subdir);
|
|
43875
|
-
if (!existsSync15(subdirPath)) {
|
|
43876
|
-
mkdirSync7(subdirPath, { recursive: true });
|
|
43877
|
-
}
|
|
43878
|
-
}
|
|
43879
|
-
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43880
|
-
if (!existsSync15(locusMdPath)) {
|
|
43881
|
-
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
43882
|
-
}
|
|
43883
|
-
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
43884
|
-
if (!existsSync15(learningsMdPath)) {
|
|
43885
|
-
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
43886
|
-
}
|
|
43887
|
-
if (!existsSync15(locusConfigPath)) {
|
|
43888
|
-
const config2 = {
|
|
43889
|
-
$schema: LOCUS_SCHEMAS.config,
|
|
43890
|
-
version: version2,
|
|
43891
|
-
createdAt: new Date().toISOString(),
|
|
43892
|
-
projectPath: "."
|
|
43893
|
-
};
|
|
43894
|
-
writeFileSync7(locusConfigPath, JSON.stringify(config2, null, 2));
|
|
43895
|
-
}
|
|
43896
|
-
updateGitignore(this.projectPath);
|
|
43897
|
-
ensureGitIdentity(this.projectPath);
|
|
43935
|
+
if (settings.provider) {
|
|
43936
|
+
console.log(` ${c.primary("provider:")} ${settings.provider}`);
|
|
43898
43937
|
}
|
|
43899
|
-
|
|
43900
|
-
|
|
43901
|
-
if (existsSync15(path3)) {
|
|
43902
|
-
return JSON.parse(readFileSync13(path3, "utf-8"));
|
|
43903
|
-
}
|
|
43904
|
-
return null;
|
|
43938
|
+
if (settings.model) {
|
|
43939
|
+
console.log(` ${c.primary("model:")} ${settings.model}`);
|
|
43905
43940
|
}
|
|
43906
|
-
|
|
43907
|
-
|
|
43908
|
-
if (config2 && config2.version !== version2) {
|
|
43909
|
-
config2.version = version2;
|
|
43910
|
-
this.saveConfig(config2);
|
|
43911
|
-
}
|
|
43941
|
+
if (settings.workspaceId) {
|
|
43942
|
+
console.log(` ${c.primary("workspaceId:")} ${settings.workspaceId}`);
|
|
43912
43943
|
}
|
|
43913
|
-
|
|
43914
|
-
const
|
|
43915
|
-
|
|
43916
|
-
|
|
43917
|
-
|
|
43918
|
-
|
|
43919
|
-
};
|
|
43920
|
-
const config2 = this.loadConfig();
|
|
43921
|
-
if (config2) {
|
|
43922
|
-
result.previousVersion = config2.version;
|
|
43923
|
-
const needsSchemaUpdate = config2.$schema !== LOCUS_SCHEMAS.config;
|
|
43924
|
-
if (config2.version !== version2) {
|
|
43925
|
-
config2.version = version2;
|
|
43926
|
-
result.versionUpdated = true;
|
|
43927
|
-
}
|
|
43928
|
-
if (result.versionUpdated || needsSchemaUpdate) {
|
|
43929
|
-
this.saveConfig(config2);
|
|
43930
|
-
}
|
|
43931
|
-
}
|
|
43932
|
-
const settingsPath = join15(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
43933
|
-
if (existsSync15(settingsPath)) {
|
|
43934
|
-
const raw = readFileSync13(settingsPath, "utf-8");
|
|
43935
|
-
const settings = JSON.parse(raw);
|
|
43936
|
-
if (settings.$schema !== LOCUS_SCHEMAS.settings) {
|
|
43937
|
-
const { $schema: _2, ...rest } = settings;
|
|
43938
|
-
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
43939
|
-
writeFileSync7(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
43940
|
-
}
|
|
43941
|
-
}
|
|
43942
|
-
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43943
|
-
const locusMdExisted = existsSync15(locusMdPath);
|
|
43944
|
-
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
43945
|
-
if (!locusMdExisted) {
|
|
43946
|
-
result.directoriesCreated.push(".locus/LOCUS.md");
|
|
43947
|
-
}
|
|
43948
|
-
const locusSubdirs = [
|
|
43949
|
-
LOCUS_CONFIG.artifactsDir,
|
|
43950
|
-
LOCUS_CONFIG.documentsDir,
|
|
43951
|
-
LOCUS_CONFIG.sessionsDir,
|
|
43952
|
-
LOCUS_CONFIG.reviewsDir,
|
|
43953
|
-
LOCUS_CONFIG.plansDir,
|
|
43954
|
-
LOCUS_CONFIG.discussionsDir
|
|
43955
|
-
];
|
|
43956
|
-
const locusConfigDir = join15(this.projectPath, LOCUS_CONFIG.dir);
|
|
43957
|
-
for (const subdir of locusSubdirs) {
|
|
43958
|
-
const subdirPath = join15(locusConfigDir, subdir);
|
|
43959
|
-
if (!existsSync15(subdirPath)) {
|
|
43960
|
-
mkdirSync7(subdirPath, { recursive: true });
|
|
43961
|
-
result.directoriesCreated.push(`.locus/${subdir}`);
|
|
43962
|
-
}
|
|
43944
|
+
if (settings.telegram) {
|
|
43945
|
+
const tg = settings.telegram;
|
|
43946
|
+
console.log(`
|
|
43947
|
+
${c.header(" TELEGRAM ")}`);
|
|
43948
|
+
if (tg.botToken) {
|
|
43949
|
+
console.log(` ${c.primary("botToken:")} ${maskSecret(tg.botToken)}`);
|
|
43963
43950
|
}
|
|
43964
|
-
|
|
43965
|
-
|
|
43966
|
-
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
43967
|
-
result.directoriesCreated.push(".locus/LEARNINGS.md");
|
|
43951
|
+
if (tg.chatId) {
|
|
43952
|
+
console.log(` ${c.primary("chatId:")} ${tg.chatId}`);
|
|
43968
43953
|
}
|
|
43969
|
-
|
|
43970
|
-
|
|
43971
|
-
updateGitignore(this.projectPath);
|
|
43972
|
-
const gitignoreAfter = readFileSync13(gitignorePath, "utf-8");
|
|
43973
|
-
if (gitignoreBefore !== gitignoreAfter) {
|
|
43974
|
-
result.gitignoreUpdated = true;
|
|
43954
|
+
if (tg.testMode !== undefined) {
|
|
43955
|
+
console.log(` ${c.primary("testMode:")} ${tg.testMode}`);
|
|
43975
43956
|
}
|
|
43976
|
-
ensureGitIdentity(this.projectPath);
|
|
43977
|
-
return result;
|
|
43978
|
-
}
|
|
43979
|
-
saveConfig(config2) {
|
|
43980
|
-
const { $schema: _2, ...rest } = config2;
|
|
43981
|
-
const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
|
|
43982
|
-
const path3 = getLocusPath(this.projectPath, "configFile");
|
|
43983
|
-
writeFileSync7(path3, JSON.stringify(ordered, null, 2));
|
|
43984
43957
|
}
|
|
43958
|
+
console.log("");
|
|
43985
43959
|
}
|
|
43986
|
-
|
|
43987
|
-
|
|
43988
|
-
|
|
43989
|
-
|
|
43990
|
-
|
|
43991
|
-
|
|
43992
|
-
|
|
43993
|
-
|
|
43960
|
+
function setCommand(args, projectPath) {
|
|
43961
|
+
const key = args[0]?.trim();
|
|
43962
|
+
const value = args.slice(1).join(" ").trim();
|
|
43963
|
+
if (!key || !value) {
|
|
43964
|
+
console.error(`
|
|
43965
|
+
${c.error("✖")} ${c.bold("Usage:")} locus config set <key> <value>
|
|
43966
|
+
` + ` ${c.dim(`Available keys: ${ALL_KEYS.join(", ")}`)}
|
|
43967
|
+
`);
|
|
43968
|
+
process.exit(1);
|
|
43994
43969
|
}
|
|
43995
|
-
|
|
43996
|
-
|
|
43997
|
-
|
|
43970
|
+
if (!ALL_KEYS.includes(key)) {
|
|
43971
|
+
console.error(`
|
|
43972
|
+
${c.error("✖")} ${c.bold(`Unknown key: ${key}`)}
|
|
43973
|
+
` + ` ${c.dim(`Available keys: ${ALL_KEYS.join(", ")}`)}
|
|
43974
|
+
`);
|
|
43975
|
+
process.exit(1);
|
|
43976
|
+
}
|
|
43977
|
+
const manager = new SettingsManager(projectPath);
|
|
43978
|
+
const settings = manager.load();
|
|
43979
|
+
if (key.startsWith("telegram.")) {
|
|
43980
|
+
const telegramKey = key.replace("telegram.", "");
|
|
43981
|
+
if (!settings.telegram) {
|
|
43982
|
+
settings.telegram = {};
|
|
43998
43983
|
}
|
|
43999
|
-
|
|
44000
|
-
|
|
44001
|
-
|
|
44002
|
-
|
|
44003
|
-
|
|
44004
|
-
|
|
44005
|
-
|
|
44006
|
-
if (info.workspaceId) {
|
|
44007
|
-
console.log(c.success(`✓ Resolved workspace: ${info.workspaceId}`));
|
|
44008
|
-
return info.workspaceId;
|
|
43984
|
+
if (telegramKey === "chatId") {
|
|
43985
|
+
const num = Number(value);
|
|
43986
|
+
if (Number.isNaN(num)) {
|
|
43987
|
+
console.error(`
|
|
43988
|
+
${c.error("✖")} ${c.bold(`${key} must be a number.`)}
|
|
43989
|
+
`);
|
|
43990
|
+
process.exit(1);
|
|
44009
43991
|
}
|
|
44010
|
-
|
|
44011
|
-
}
|
|
44012
|
-
|
|
43992
|
+
settings.telegram[telegramKey] = num;
|
|
43993
|
+
} else if (telegramKey === "testMode") {
|
|
43994
|
+
settings.telegram[telegramKey] = value === "true" || value === "1";
|
|
43995
|
+
} else {
|
|
43996
|
+
settings.telegram[telegramKey] = value;
|
|
44013
43997
|
}
|
|
43998
|
+
} else {
|
|
43999
|
+
settings[key] = value;
|
|
44000
|
+
}
|
|
44001
|
+
manager.save(settings);
|
|
44002
|
+
const displayValue = key === "apiKey" || key === "telegram.botToken" ? maskSecret(value) : value;
|
|
44003
|
+
console.log(`
|
|
44004
|
+
${c.success("✔")} Set ${c.primary(key)} = ${displayValue}
|
|
44005
|
+
`);
|
|
44006
|
+
}
|
|
44007
|
+
function removeCommand(projectPath) {
|
|
44008
|
+
const manager = new SettingsManager(projectPath);
|
|
44009
|
+
if (!manager.exists()) {
|
|
44010
|
+
console.log(`
|
|
44011
|
+
${c.dim("No settings found. Nothing to remove.")}
|
|
44012
|
+
`);
|
|
44013
|
+
return;
|
|
44014
44014
|
}
|
|
44015
|
+
manager.remove();
|
|
44016
|
+
console.log(`
|
|
44017
|
+
${c.success("✔")} ${c.bold("Settings removed.")}
|
|
44018
|
+
`);
|
|
44015
44019
|
}
|
|
44016
|
-
|
|
44017
|
-
|
|
44018
|
-
async function docsCommand(args) {
|
|
44020
|
+
async function configCommand(args) {
|
|
44021
|
+
const projectPath = process.cwd();
|
|
44019
44022
|
const subcommand = args[0];
|
|
44020
44023
|
const subArgs = args.slice(1);
|
|
44021
44024
|
switch (subcommand) {
|
|
44022
|
-
case "
|
|
44023
|
-
await
|
|
44025
|
+
case "setup":
|
|
44026
|
+
await setupCommand(subArgs, projectPath);
|
|
44024
44027
|
break;
|
|
44025
|
-
|
|
44026
|
-
|
|
44028
|
+
case "show":
|
|
44029
|
+
showCommand(projectPath);
|
|
44030
|
+
break;
|
|
44031
|
+
case "set":
|
|
44032
|
+
setCommand(subArgs, projectPath);
|
|
44033
|
+
break;
|
|
44034
|
+
case "remove":
|
|
44035
|
+
removeCommand(projectPath);
|
|
44027
44036
|
break;
|
|
44037
|
+
default:
|
|
44038
|
+
showConfigHelp();
|
|
44028
44039
|
}
|
|
44029
44040
|
}
|
|
44030
|
-
|
|
44031
|
-
|
|
44041
|
+
// src/commands/discuss.ts
|
|
44042
|
+
init_index_node();
|
|
44043
|
+
init_progress_renderer();
|
|
44044
|
+
init_settings_manager();
|
|
44045
|
+
init_utils3();
|
|
44046
|
+
import * as readline2 from "node:readline";
|
|
44047
|
+
import { parseArgs as parseArgs3 } from "node:util";
|
|
44048
|
+
async function discussCommand(args) {
|
|
44049
|
+
const { values, positionals } = parseArgs3({
|
|
44032
44050
|
args,
|
|
44033
44051
|
options: {
|
|
44034
|
-
|
|
44035
|
-
|
|
44036
|
-
|
|
44037
|
-
|
|
44038
|
-
|
|
44052
|
+
list: { type: "boolean" },
|
|
44053
|
+
show: { type: "string" },
|
|
44054
|
+
archive: { type: "string" },
|
|
44055
|
+
delete: { type: "string" },
|
|
44056
|
+
model: { type: "string" },
|
|
44057
|
+
provider: { type: "string" },
|
|
44058
|
+
"reasoning-effort": { type: "string" },
|
|
44059
|
+
dir: { type: "string" }
|
|
44039
44060
|
},
|
|
44040
|
-
strict: false
|
|
44061
|
+
strict: false,
|
|
44062
|
+
allowPositionals: true
|
|
44041
44063
|
});
|
|
44042
|
-
|
|
44043
|
-
|
|
44064
|
+
const projectPath = values.dir || process.cwd();
|
|
44065
|
+
requireInitialization(projectPath, "discuss");
|
|
44066
|
+
const discussionManager = new DiscussionManager(projectPath);
|
|
44067
|
+
if (values.list) {
|
|
44068
|
+
return listDiscussions(discussionManager);
|
|
44069
|
+
}
|
|
44070
|
+
if (values.show) {
|
|
44071
|
+
return showDiscussion(discussionManager, values.show);
|
|
44072
|
+
}
|
|
44073
|
+
if (values.archive) {
|
|
44074
|
+
return archiveDiscussion(discussionManager, values.archive);
|
|
44075
|
+
}
|
|
44076
|
+
if (values.delete) {
|
|
44077
|
+
return deleteDiscussion(discussionManager, values.delete);
|
|
44078
|
+
}
|
|
44079
|
+
const topic = positionals.join(" ").trim();
|
|
44080
|
+
if (!topic) {
|
|
44081
|
+
showDiscussHelp();
|
|
44082
|
+
return;
|
|
44083
|
+
}
|
|
44084
|
+
const settings = new SettingsManager(projectPath).load();
|
|
44085
|
+
const provider = resolveProvider3(values.provider || settings.provider);
|
|
44086
|
+
const model = values.model || settings.model || DEFAULT_MODEL[provider];
|
|
44087
|
+
const reasoningEffort = values["reasoning-effort"];
|
|
44088
|
+
const aiRunner = createAiRunner(provider, {
|
|
44089
|
+
projectPath,
|
|
44090
|
+
model,
|
|
44091
|
+
reasoningEffort
|
|
44092
|
+
});
|
|
44093
|
+
const log = (message, level) => {
|
|
44094
|
+
const icon = level === "success" ? c.success("✔") : level === "error" ? c.error("✖") : level === "warn" ? c.warning("!") : c.info("●");
|
|
44095
|
+
console.log(` ${icon} ${message}`);
|
|
44096
|
+
};
|
|
44097
|
+
const facilitator = new DiscussionFacilitator({
|
|
44098
|
+
projectPath,
|
|
44099
|
+
aiRunner,
|
|
44100
|
+
discussionManager,
|
|
44101
|
+
log,
|
|
44102
|
+
provider,
|
|
44103
|
+
model
|
|
44104
|
+
});
|
|
44105
|
+
console.log(`
|
|
44106
|
+
${c.header(" DISCUSSION ")} ${c.bold("Starting interactive discussion...")}
|
|
44107
|
+
`);
|
|
44108
|
+
console.log(` ${c.dim("Topic:")} ${c.bold(topic)}`);
|
|
44109
|
+
console.log(` ${c.dim("Model:")} ${c.dim(`${model} (${provider})`)}
|
|
44110
|
+
`);
|
|
44111
|
+
const renderer = new ProgressRenderer({ animated: true });
|
|
44112
|
+
let discussionId;
|
|
44113
|
+
try {
|
|
44114
|
+
renderer.showThinkingStarted();
|
|
44115
|
+
const result = await facilitator.startDiscussion(topic);
|
|
44116
|
+
renderer.showThinkingStopped();
|
|
44117
|
+
discussionId = result.discussion.id;
|
|
44118
|
+
process.stdout.write(`
|
|
44119
|
+
`);
|
|
44120
|
+
process.stdout.write(result.message);
|
|
44121
|
+
process.stdout.write(`
|
|
44122
|
+
|
|
44123
|
+
`);
|
|
44124
|
+
renderer.finalize();
|
|
44125
|
+
} catch (error48) {
|
|
44126
|
+
renderer.finalize();
|
|
44127
|
+
console.error(`
|
|
44128
|
+
${c.error("✖")} ${c.red("Failed to start discussion:")} ${error48 instanceof Error ? error48.message : String(error48)}
|
|
44129
|
+
`);
|
|
44130
|
+
process.exit(1);
|
|
44131
|
+
}
|
|
44132
|
+
console.log(` ${c.dim("Type your response, or 'help' for commands. Use 'exit' or Ctrl+C to quit.")}
|
|
44133
|
+
`);
|
|
44134
|
+
const rl = readline2.createInterface({
|
|
44135
|
+
input: process.stdin,
|
|
44136
|
+
output: process.stdout,
|
|
44137
|
+
terminal: true
|
|
44138
|
+
});
|
|
44139
|
+
rl.setPrompt(c.cyan("> "));
|
|
44140
|
+
rl.prompt();
|
|
44141
|
+
let isProcessing = false;
|
|
44142
|
+
const shutdown = () => {
|
|
44143
|
+
if (isProcessing) {
|
|
44144
|
+
aiRunner.abort();
|
|
44145
|
+
}
|
|
44146
|
+
console.log(`
|
|
44147
|
+
${c.dim("Discussion saved.")} ${c.dim("ID:")} ${c.cyan(discussionId)}`);
|
|
44148
|
+
console.log(c.dim(`
|
|
44149
|
+
Goodbye!
|
|
44150
|
+
`));
|
|
44151
|
+
rl.close();
|
|
44152
|
+
process.exit(0);
|
|
44153
|
+
};
|
|
44154
|
+
process.on("SIGINT", () => {
|
|
44155
|
+
if (isProcessing) {
|
|
44156
|
+
aiRunner.abort();
|
|
44157
|
+
isProcessing = false;
|
|
44158
|
+
console.log(c.dim(`
|
|
44159
|
+
[Interrupted]`));
|
|
44160
|
+
rl.prompt();
|
|
44161
|
+
} else {
|
|
44162
|
+
shutdown();
|
|
44163
|
+
}
|
|
44164
|
+
});
|
|
44165
|
+
rl.on("close", () => {
|
|
44166
|
+
shutdown();
|
|
44167
|
+
});
|
|
44168
|
+
rl.on("line", async (input) => {
|
|
44169
|
+
const trimmed = input.trim();
|
|
44170
|
+
if (trimmed === "" || isProcessing) {
|
|
44171
|
+
rl.prompt();
|
|
44172
|
+
return;
|
|
44173
|
+
}
|
|
44174
|
+
const lowerInput = trimmed.toLowerCase();
|
|
44175
|
+
if (lowerInput === "help") {
|
|
44176
|
+
showReplHelp();
|
|
44177
|
+
rl.prompt();
|
|
44178
|
+
return;
|
|
44179
|
+
}
|
|
44180
|
+
if (lowerInput === "exit" || lowerInput === "quit") {
|
|
44181
|
+
shutdown();
|
|
44182
|
+
return;
|
|
44183
|
+
}
|
|
44184
|
+
if (lowerInput === "insights") {
|
|
44185
|
+
showCurrentInsights(discussionManager, discussionId);
|
|
44186
|
+
rl.prompt();
|
|
44187
|
+
return;
|
|
44188
|
+
}
|
|
44189
|
+
if (lowerInput === "summary") {
|
|
44190
|
+
isProcessing = true;
|
|
44191
|
+
const summaryRenderer = new ProgressRenderer({ animated: true });
|
|
44192
|
+
try {
|
|
44193
|
+
summaryRenderer.showThinkingStarted();
|
|
44194
|
+
const summary = await facilitator.summarizeDiscussion(discussionId);
|
|
44195
|
+
summaryRenderer.showThinkingStopped();
|
|
44196
|
+
process.stdout.write(`
|
|
44197
|
+
`);
|
|
44198
|
+
process.stdout.write(summary);
|
|
44199
|
+
process.stdout.write(`
|
|
44200
|
+
`);
|
|
44201
|
+
summaryRenderer.finalize();
|
|
44202
|
+
const discussion2 = discussionManager.load(discussionId);
|
|
44203
|
+
if (discussion2) {
|
|
44204
|
+
console.log(`
|
|
44205
|
+
${c.success("✔")} ${c.success("Discussion completed!")}
|
|
44206
|
+
`);
|
|
44207
|
+
console.log(` ${c.dim("Messages:")} ${discussion2.messages.length} ${c.dim("Insights:")} ${discussion2.insights.length}
|
|
44208
|
+
`);
|
|
44209
|
+
}
|
|
44210
|
+
console.log(` ${c.dim("To review:")} ${c.cyan(`locus discuss --show ${discussionId}`)}`);
|
|
44211
|
+
console.log(` ${c.dim("To list all:")} ${c.cyan("locus discuss --list")}
|
|
44212
|
+
`);
|
|
44213
|
+
} catch (error48) {
|
|
44214
|
+
summaryRenderer.finalize();
|
|
44215
|
+
console.error(`
|
|
44216
|
+
${c.error("✖")} ${c.red("Failed to summarize:")} ${error48 instanceof Error ? error48.message : String(error48)}
|
|
44217
|
+
`);
|
|
44218
|
+
}
|
|
44219
|
+
rl.close();
|
|
44220
|
+
process.exit(0);
|
|
44221
|
+
return;
|
|
44222
|
+
}
|
|
44223
|
+
isProcessing = true;
|
|
44224
|
+
const chunkRenderer = new ProgressRenderer({ animated: true });
|
|
44225
|
+
try {
|
|
44226
|
+
chunkRenderer.showThinkingStarted();
|
|
44227
|
+
const stream4 = facilitator.continueDiscussionStream(discussionId, trimmed);
|
|
44228
|
+
let result = {
|
|
44229
|
+
response: "",
|
|
44230
|
+
insights: []
|
|
44231
|
+
};
|
|
44232
|
+
let iterResult = await stream4.next();
|
|
44233
|
+
while (!iterResult.done) {
|
|
44234
|
+
chunkRenderer.renderChunk(iterResult.value);
|
|
44235
|
+
iterResult = await stream4.next();
|
|
44236
|
+
}
|
|
44237
|
+
result = iterResult.value;
|
|
44238
|
+
chunkRenderer.finalize();
|
|
44239
|
+
if (result.insights.length > 0) {
|
|
44240
|
+
console.log("");
|
|
44241
|
+
for (const insight of result.insights) {
|
|
44242
|
+
const tag = formatInsightTag(insight.type);
|
|
44243
|
+
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
44244
|
+
console.log(` ${c.dim(insight.content)}
|
|
44245
|
+
`);
|
|
44246
|
+
}
|
|
44247
|
+
}
|
|
44248
|
+
} catch (error48) {
|
|
44249
|
+
chunkRenderer.finalize();
|
|
44250
|
+
console.error(`
|
|
44251
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
44252
|
+
`);
|
|
44253
|
+
}
|
|
44254
|
+
isProcessing = false;
|
|
44255
|
+
rl.prompt();
|
|
44256
|
+
});
|
|
44257
|
+
}
|
|
44258
|
+
function listDiscussions(discussionManager) {
|
|
44259
|
+
const discussions = discussionManager.list();
|
|
44260
|
+
if (discussions.length === 0) {
|
|
44261
|
+
console.log(`
|
|
44262
|
+
${c.dim("No discussions found.")}
|
|
44263
|
+
`);
|
|
44264
|
+
console.log(` ${c.dim("Start one with:")} ${c.cyan('locus discuss "your topic"')}
|
|
44265
|
+
`);
|
|
44044
44266
|
return;
|
|
44045
44267
|
}
|
|
44046
|
-
|
|
44047
|
-
|
|
44048
|
-
|
|
44049
|
-
|
|
44050
|
-
|
|
44051
|
-
|
|
44052
|
-
|
|
44053
|
-
|
|
44268
|
+
console.log(`
|
|
44269
|
+
${c.header(" DISCUSSIONS ")} ${c.dim(`(${discussions.length})`)}
|
|
44270
|
+
`);
|
|
44271
|
+
for (const disc of discussions) {
|
|
44272
|
+
const statusIcon = disc.status === "active" ? c.warning("◯") : disc.status === "completed" ? c.success("✔") : c.dim("⊘");
|
|
44273
|
+
console.log(` ${statusIcon} ${c.bold(disc.title)} ${c.dim(`[${disc.status}]`)} ${c.dim(`— ${disc.messages.length} messages, ${disc.insights.length} insights`)}`);
|
|
44274
|
+
console.log(` ${c.dim("ID:")} ${disc.id}`);
|
|
44275
|
+
console.log(` ${c.dim("Created:")} ${disc.createdAt}`);
|
|
44276
|
+
console.log("");
|
|
44277
|
+
}
|
|
44278
|
+
}
|
|
44279
|
+
function showDiscussion(discussionManager, id) {
|
|
44280
|
+
const md = discussionManager.getMarkdown(id);
|
|
44281
|
+
if (!md) {
|
|
44054
44282
|
console.error(`
|
|
44055
|
-
${c.error("✖")} ${c.red(
|
|
44056
|
-
` + ` ${c.dim(`Configure with: locus config setup --api-key <key>
|
|
44057
|
-
Or pass --api-key flag`)}
|
|
44283
|
+
${c.error("✖")} ${c.red(`Discussion not found: ${id}`)}
|
|
44058
44284
|
`);
|
|
44059
44285
|
process.exit(1);
|
|
44060
44286
|
}
|
|
44061
|
-
|
|
44062
|
-
|
|
44063
|
-
|
|
44064
|
-
|
|
44065
|
-
|
|
44066
|
-
});
|
|
44067
|
-
let workspaceId;
|
|
44287
|
+
console.log(`
|
|
44288
|
+
${md}
|
|
44289
|
+
`);
|
|
44290
|
+
}
|
|
44291
|
+
function archiveDiscussion(discussionManager, id) {
|
|
44068
44292
|
try {
|
|
44069
|
-
|
|
44293
|
+
discussionManager.archive(id);
|
|
44294
|
+
console.log(`
|
|
44295
|
+
${c.success("✔")} ${c.dim("Discussion archived.")}
|
|
44296
|
+
`);
|
|
44070
44297
|
} catch (error48) {
|
|
44071
44298
|
console.error(`
|
|
44072
44299
|
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
44073
44300
|
`);
|
|
44074
44301
|
process.exit(1);
|
|
44075
44302
|
}
|
|
44076
|
-
|
|
44077
|
-
|
|
44078
|
-
token: apiKey
|
|
44079
|
-
});
|
|
44080
|
-
const fetcher = new DocumentFetcher({
|
|
44081
|
-
client,
|
|
44082
|
-
workspaceId,
|
|
44083
|
-
projectPath,
|
|
44084
|
-
log: (message, level) => {
|
|
44085
|
-
if (level === "error") {
|
|
44086
|
-
console.log(` ${c.error("✖")} ${message}`);
|
|
44087
|
-
return;
|
|
44088
|
-
}
|
|
44089
|
-
if (level === "warn") {
|
|
44090
|
-
console.log(` ${c.warning("!")} ${message}`);
|
|
44091
|
-
return;
|
|
44092
|
-
}
|
|
44093
|
-
if (level === "success") {
|
|
44094
|
-
console.log(` ${c.success("✔")} ${message}`);
|
|
44095
|
-
return;
|
|
44096
|
-
}
|
|
44097
|
-
console.log(` ${c.info("●")} ${message}`);
|
|
44098
|
-
}
|
|
44099
|
-
});
|
|
44100
|
-
console.log(`
|
|
44101
|
-
${c.info("●")} ${c.bold("Syncing docs from API...")}
|
|
44102
|
-
`);
|
|
44303
|
+
}
|
|
44304
|
+
function deleteDiscussion(discussionManager, id) {
|
|
44103
44305
|
try {
|
|
44104
|
-
|
|
44306
|
+
discussionManager.delete(id);
|
|
44105
44307
|
console.log(`
|
|
44106
|
-
${c.success("✔")} ${c.
|
|
44308
|
+
${c.success("✔")} ${c.dim("Discussion deleted.")}
|
|
44107
44309
|
`);
|
|
44108
44310
|
} catch (error48) {
|
|
44109
44311
|
console.error(`
|
|
44110
|
-
${c.error("✖")} ${c.red(
|
|
44312
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
44111
44313
|
`);
|
|
44112
44314
|
process.exit(1);
|
|
44113
44315
|
}
|
|
44114
44316
|
}
|
|
44115
|
-
function
|
|
44317
|
+
function showCurrentInsights(discussionManager, discussionId) {
|
|
44318
|
+
const discussion2 = discussionManager.load(discussionId);
|
|
44319
|
+
if (!discussion2 || discussion2.insights.length === 0) {
|
|
44320
|
+
console.log(`
|
|
44321
|
+
${c.dim("No insights extracted yet.")}
|
|
44322
|
+
`);
|
|
44323
|
+
return;
|
|
44324
|
+
}
|
|
44116
44325
|
console.log(`
|
|
44117
|
-
${c.header("
|
|
44118
|
-
|
|
44326
|
+
${c.header(" INSIGHTS ")} ${c.dim(`(${discussion2.insights.length})`)}
|
|
44327
|
+
`);
|
|
44328
|
+
for (const insight of discussion2.insights) {
|
|
44329
|
+
const tag = formatInsightTag(insight.type);
|
|
44330
|
+
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
44331
|
+
console.log(` ${c.dim(insight.content)}`);
|
|
44332
|
+
if (insight.tags.length > 0) {
|
|
44333
|
+
console.log(` ${c.dim(`Tags: ${insight.tags.join(", ")}`)}`);
|
|
44334
|
+
}
|
|
44335
|
+
console.log("");
|
|
44336
|
+
}
|
|
44337
|
+
}
|
|
44338
|
+
function formatInsightTag(type) {
|
|
44339
|
+
switch (type) {
|
|
44340
|
+
case "decision":
|
|
44341
|
+
return c.green("[DECISION]");
|
|
44342
|
+
case "requirement":
|
|
44343
|
+
return c.blue("[REQUIREMENT]");
|
|
44344
|
+
case "idea":
|
|
44345
|
+
return c.yellow("[IDEA]");
|
|
44346
|
+
case "concern":
|
|
44347
|
+
return c.red("[CONCERN]");
|
|
44348
|
+
case "learning":
|
|
44349
|
+
return c.cyan("[LEARNING]");
|
|
44350
|
+
}
|
|
44351
|
+
}
|
|
44352
|
+
function showReplHelp() {
|
|
44353
|
+
console.log(`
|
|
44354
|
+
${c.header(" DISCUSSION COMMANDS ")}
|
|
44119
44355
|
|
|
44120
|
-
|
|
44121
|
-
${c.
|
|
44356
|
+
${c.cyan("summary")} Generate a final summary and end the discussion
|
|
44357
|
+
${c.cyan("insights")} Show all insights extracted so far
|
|
44358
|
+
${c.cyan("exit")} Save and exit without generating a summary
|
|
44359
|
+
${c.cyan("help")} Show this help message
|
|
44122
44360
|
|
|
44123
|
-
${c.
|
|
44124
|
-
${c.dim("$")} ${c.primary("locus docs sync")}
|
|
44125
|
-
${c.dim("$")} ${c.primary("locus docs sync --workspace ws_123")}
|
|
44361
|
+
${c.dim("Type anything else to continue the discussion.")}
|
|
44126
44362
|
`);
|
|
44127
44363
|
}
|
|
44128
|
-
function
|
|
44364
|
+
function showDiscussHelp() {
|
|
44129
44365
|
console.log(`
|
|
44130
|
-
${c.header("
|
|
44131
|
-
${c.primary("locus docs sync")} ${c.dim("[options]")}
|
|
44366
|
+
${c.header(" LOCUS DISCUSS ")} ${c.dim("— Interactive AI Discussion")}
|
|
44132
44367
|
|
|
44133
|
-
${c.
|
|
44134
|
-
${c.
|
|
44135
|
-
${c.
|
|
44136
|
-
${c.
|
|
44137
|
-
${c.
|
|
44138
|
-
${c.
|
|
44368
|
+
${c.bold("Usage:")}
|
|
44369
|
+
${c.cyan('locus discuss "topic"')} Start a discussion on a topic
|
|
44370
|
+
${c.cyan("locus discuss --list")} List all discussions
|
|
44371
|
+
${c.cyan("locus discuss --show <id>")} Show discussion details
|
|
44372
|
+
${c.cyan("locus discuss --archive <id>")} Archive a discussion
|
|
44373
|
+
${c.cyan("locus discuss --delete <id>")} Delete a discussion
|
|
44374
|
+
|
|
44375
|
+
${c.bold("Options:")}
|
|
44376
|
+
${c.dim("--model <model>")} AI model (claude: opus, sonnet, haiku | codex: gpt-5.3-codex, gpt-5-codex-mini)
|
|
44377
|
+
${c.dim("--provider <p>")} AI provider (claude, codex)
|
|
44378
|
+
${c.dim("--reasoning-effort <level>")} Reasoning effort (low, medium, high)
|
|
44379
|
+
${c.dim("--dir <path>")} Project directory
|
|
44380
|
+
|
|
44381
|
+
${c.bold("REPL Commands:")}
|
|
44382
|
+
${c.dim("summary")} Generate final summary and end the discussion
|
|
44383
|
+
${c.dim("insights")} Show all insights extracted so far
|
|
44384
|
+
${c.dim("exit")} Save and exit without generating a summary
|
|
44385
|
+
${c.dim("help")} Show available commands
|
|
44386
|
+
|
|
44387
|
+
${c.bold("Examples:")}
|
|
44388
|
+
${c.dim("# Start a discussion about architecture")}
|
|
44389
|
+
${c.cyan('locus discuss "how should we structure the auth system?"')}
|
|
44390
|
+
|
|
44391
|
+
${c.dim("# Review a past discussion")}
|
|
44392
|
+
${c.cyan("locus discuss --show disc-1234567890")}
|
|
44393
|
+
|
|
44394
|
+
${c.dim("# List all discussions")}
|
|
44395
|
+
${c.cyan("locus discuss --list")}
|
|
44396
|
+
`);
|
|
44397
|
+
}
|
|
44398
|
+
// src/commands/docs.ts
|
|
44399
|
+
init_index_node();
|
|
44400
|
+
import { parseArgs as parseArgs4 } from "node:util";
|
|
44401
|
+
|
|
44402
|
+
// src/config-manager.ts
|
|
44403
|
+
init_index_node();
|
|
44404
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
44405
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
|
|
44406
|
+
import { join as join16 } from "node:path";
|
|
44407
|
+
var LOCUS_GITIGNORE_MARKER = "# Locus AI";
|
|
44408
|
+
var LOCUS_MD_TEMPLATE = `## Planning First
|
|
44409
|
+
|
|
44410
|
+
Complex tasks must be planned before writing code. Create ".locus/plans/<task-name>.md" with:
|
|
44411
|
+
- **Goal**: What we're trying to achieve and why
|
|
44412
|
+
- **Approach**: Step-by-step strategy with technical decisions
|
|
44413
|
+
- **Affected files**: List of files to create/modify/delete
|
|
44414
|
+
- **Acceptance criteria**: Specific, testable conditions for completion
|
|
44415
|
+
- **Dependencies**: Required packages, APIs, or external services
|
|
44416
|
+
|
|
44417
|
+
Delete the planning .md files after successful execution.
|
|
44418
|
+
|
|
44419
|
+
## Code Quality
|
|
44420
|
+
|
|
44421
|
+
- **Follow existing patterns**: Run formatters and linters before finishing (check "package.json" scripts or project config)
|
|
44422
|
+
- **Minimize changes**: Keep modifications atomic. Separate refactors from behavioral changes into different tasks
|
|
44423
|
+
- **Never commit secrets**: No API keys, passwords, or credentials in code. Use environment variables or secret management
|
|
44424
|
+
- **Test as you go**: If tests exist, run relevant ones. If breaking changes occur, update tests accordingly
|
|
44425
|
+
- **Comment complex logic**: Explain *why*, not *what*. Focus on business logic and non-obvious decisions
|
|
44426
|
+
|
|
44427
|
+
## Artifacts
|
|
44428
|
+
|
|
44429
|
+
When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save results as Markdown in ".locus/artifacts/<descriptive-name>.md":
|
|
44430
|
+
|
|
44431
|
+
**Always create artifacts for:**
|
|
44432
|
+
- Code quality audits, security reviews, vulnerability assessments
|
|
44433
|
+
- Architecture analyses, system design proposals, or recommendations
|
|
44434
|
+
- Dependency reports, performance profiling, benchmarking results
|
|
44435
|
+
- Research summaries, technology comparisons, or feasibility studies
|
|
44436
|
+
- Migration plans, deployment strategies, or rollback procedures
|
|
44437
|
+
- Post-mortems, incident analysis, or debugging investigations
|
|
44438
|
+
|
|
44439
|
+
**Artifact structure:**
|
|
44440
|
+
- Clear title and date
|
|
44441
|
+
- Executive summary (2-3 sentences)
|
|
44442
|
+
- Detailed findings/analysis
|
|
44443
|
+
- Actionable recommendations (if applicable)
|
|
44444
|
+
|
|
44445
|
+
## Git Operations
|
|
44446
|
+
|
|
44447
|
+
- **Do NOT run**: git add, git commit, git push, git checkout, git branch, or any git commands
|
|
44448
|
+
- **Why**: The Locus orchestrator handles all version control automatically after execution
|
|
44449
|
+
- **Your role**: Focus solely on making file changes. The system commits, pushes, and creates PRs
|
|
44450
|
+
|
|
44451
|
+
## Continuous Learning
|
|
44452
|
+
|
|
44453
|
+
Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
|
|
44454
|
+
|
|
44455
|
+
**When to update:**
|
|
44456
|
+
- User corrects your approach or provides guidance
|
|
44457
|
+
- You discover a better pattern while working
|
|
44458
|
+
- A decision prevents future confusion (e.g., "use X not Y because Z")
|
|
44459
|
+
- You encounter and solve a tricky problem
|
|
44460
|
+
|
|
44461
|
+
**What to record:**
|
|
44462
|
+
- Architectural decisions and their rationale
|
|
44463
|
+
- Preferred libraries, tools, or patterns for this project
|
|
44464
|
+
- Common pitfalls and how to avoid them
|
|
44465
|
+
- Project-specific conventions or user preferences
|
|
44466
|
+
- Solutions to non-obvious problems
|
|
44467
|
+
|
|
44468
|
+
**Format (append-only, never delete):**
|
|
44469
|
+
|
|
44470
|
+
"""
|
|
44471
|
+
- **[Category]**: Concise description (1-2 lines max). *Context if needed.*
|
|
44472
|
+
"""
|
|
44473
|
+
|
|
44474
|
+
**Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
|
|
44475
|
+
|
|
44476
|
+
## Error Handling
|
|
44477
|
+
|
|
44478
|
+
- **Read error messages carefully**: Don't guess. Parse the actual error before proposing fixes
|
|
44479
|
+
- **Check dependencies first**: Missing packages, wrong versions, and environment issues are common
|
|
44480
|
+
- **Verify assumptions**: If something "should work," confirm it actually does in this environment
|
|
44481
|
+
- **Ask for context**: If you need environment details, configuration, or logs, request them explicitly
|
|
44139
44482
|
|
|
44140
|
-
|
|
44141
|
-
${c.dim("$")} ${c.primary("locus docs sync")}
|
|
44142
|
-
${c.dim("$")} ${c.primary("locus docs sync --workspace ws_123")}
|
|
44143
|
-
`);
|
|
44144
|
-
}
|
|
44145
|
-
// src/commands/exec.ts
|
|
44146
|
-
init_index_node();
|
|
44147
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
44148
|
-
import { parseArgs as parseArgs3 } from "node:util";
|
|
44483
|
+
## Communication
|
|
44149
44484
|
|
|
44150
|
-
|
|
44151
|
-
|
|
44485
|
+
- **Be precise**: When uncertain, state what you know and what you're assuming
|
|
44486
|
+
- **Show your work**: For complex changes, briefly explain the approach before executing
|
|
44487
|
+
- **Highlight trade-offs**: If multiple approaches exist, note why you chose one over others
|
|
44488
|
+
- **Request feedback**: For ambiguous requirements, propose an approach and ask for confirmation
|
|
44489
|
+
`;
|
|
44490
|
+
var DEFAULT_LEARNINGS_MD = `# Learnings
|
|
44152
44491
|
|
|
44153
|
-
|
|
44154
|
-
|
|
44155
|
-
|
|
44156
|
-
|
|
44157
|
-
|
|
44158
|
-
|
|
44159
|
-
|
|
44160
|
-
|
|
44161
|
-
|
|
44162
|
-
|
|
44163
|
-
|
|
44164
|
-
|
|
44165
|
-
|
|
44166
|
-
|
|
44167
|
-
|
|
44168
|
-
|
|
44169
|
-
|
|
44170
|
-
|
|
44171
|
-
|
|
44172
|
-
|
|
44173
|
-
this.started = true;
|
|
44174
|
-
this.emit(createCliStreamEvent(CliStreamEventType.START, this.sessionId, {
|
|
44175
|
-
command: this.command,
|
|
44176
|
-
model: this.model,
|
|
44177
|
-
provider: this.provider,
|
|
44178
|
-
cwd: this.cwd
|
|
44179
|
-
}));
|
|
44180
|
-
}
|
|
44181
|
-
handleChunk(chunk) {
|
|
44182
|
-
this.ensureStarted();
|
|
44183
|
-
switch (chunk.type) {
|
|
44184
|
-
case "text_delta":
|
|
44185
|
-
this.emit(createCliStreamEvent(CliStreamEventType.TEXT_DELTA, this.sessionId, {
|
|
44186
|
-
content: chunk.content
|
|
44187
|
-
}));
|
|
44188
|
-
break;
|
|
44189
|
-
case "thinking":
|
|
44190
|
-
this.emit(createCliStreamEvent(CliStreamEventType.THINKING, this.sessionId, {
|
|
44191
|
-
content: chunk.content
|
|
44192
|
-
}));
|
|
44193
|
-
break;
|
|
44194
|
-
case "tool_use":
|
|
44195
|
-
this.statsTracker.toolStarted(chunk.tool, chunk.id);
|
|
44196
|
-
this.emit(createCliStreamEvent(CliStreamEventType.TOOL_STARTED, this.sessionId, {
|
|
44197
|
-
tool: chunk.tool,
|
|
44198
|
-
toolId: chunk.id,
|
|
44199
|
-
parameters: chunk.parameters
|
|
44200
|
-
}));
|
|
44201
|
-
break;
|
|
44202
|
-
case "tool_result":
|
|
44203
|
-
if (chunk.success) {
|
|
44204
|
-
this.statsTracker.toolCompleted(chunk.tool, chunk.id);
|
|
44492
|
+
This file captures important lessons, decisions, and corrections made during development.
|
|
44493
|
+
It is read by AI agents before every task to avoid repeating mistakes and to follow established patterns.
|
|
44494
|
+
|
|
44495
|
+
<!-- Add learnings below this line. Format: - **[Category]**: Description -->
|
|
44496
|
+
`;
|
|
44497
|
+
function updateGitignore(projectPath) {
|
|
44498
|
+
const gitignorePath = join16(projectPath, ".gitignore");
|
|
44499
|
+
let content = "";
|
|
44500
|
+
const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
|
|
44501
|
+
`);
|
|
44502
|
+
if (existsSync16(gitignorePath)) {
|
|
44503
|
+
content = readFileSync14(gitignorePath, "utf-8");
|
|
44504
|
+
if (content.includes(LOCUS_GITIGNORE_MARKER)) {
|
|
44505
|
+
const lines = content.split(`
|
|
44506
|
+
`);
|
|
44507
|
+
const startIdx = lines.findIndex((l) => l.includes(LOCUS_GITIGNORE_MARKER));
|
|
44508
|
+
let endIdx = startIdx;
|
|
44509
|
+
for (let i = startIdx;i < lines.length; i++) {
|
|
44510
|
+
if (lines[i].startsWith(LOCUS_GITIGNORE_MARKER) || lines[i].startsWith(".locus") || lines[i].trim() === "") {
|
|
44511
|
+
endIdx = i;
|
|
44205
44512
|
} else {
|
|
44206
|
-
|
|
44513
|
+
break;
|
|
44207
44514
|
}
|
|
44208
|
-
|
|
44209
|
-
|
|
44210
|
-
|
|
44211
|
-
|
|
44212
|
-
|
|
44213
|
-
|
|
44214
|
-
}));
|
|
44215
|
-
break;
|
|
44216
|
-
case "tool_parameters":
|
|
44217
|
-
break;
|
|
44218
|
-
case "result":
|
|
44219
|
-
break;
|
|
44220
|
-
case "error":
|
|
44221
|
-
this.statsTracker.setError(chunk.error);
|
|
44222
|
-
this.emitError("UNKNOWN", chunk.error);
|
|
44223
|
-
break;
|
|
44224
|
-
}
|
|
44225
|
-
}
|
|
44226
|
-
emitStatus(status, message) {
|
|
44227
|
-
this.ensureStarted();
|
|
44228
|
-
this.emit(createCliStreamEvent(CliStreamEventType.STATUS, this.sessionId, {
|
|
44229
|
-
status,
|
|
44230
|
-
message
|
|
44231
|
-
}));
|
|
44232
|
-
}
|
|
44233
|
-
emitError(code, message, options) {
|
|
44234
|
-
this.ensureStarted();
|
|
44235
|
-
this.emit(createCliStreamEvent(CliStreamEventType.ERROR, this.sessionId, {
|
|
44236
|
-
error: createProtocolError(code, message, options)
|
|
44237
|
-
}));
|
|
44238
|
-
}
|
|
44239
|
-
emitDone(exitCode) {
|
|
44240
|
-
if (this.done)
|
|
44515
|
+
}
|
|
44516
|
+
const before = lines.slice(0, startIdx);
|
|
44517
|
+
const after = lines.slice(endIdx + 1);
|
|
44518
|
+
content = [...before, locusBlock, ...after].join(`
|
|
44519
|
+
`);
|
|
44520
|
+
writeFileSync7(gitignorePath, content);
|
|
44241
44521
|
return;
|
|
44242
|
-
|
|
44243
|
-
|
|
44244
|
-
|
|
44245
|
-
|
|
44246
|
-
|
|
44247
|
-
|
|
44248
|
-
|
|
44249
|
-
|
|
44250
|
-
|
|
44251
|
-
}));
|
|
44252
|
-
}
|
|
44253
|
-
emitFatalError(code, message, options) {
|
|
44254
|
-
this.statsTracker.setError(message);
|
|
44255
|
-
this.emitError(code, message, {
|
|
44256
|
-
...options,
|
|
44257
|
-
recoverable: false
|
|
44258
|
-
});
|
|
44259
|
-
this.emitDone(1);
|
|
44260
|
-
}
|
|
44261
|
-
isDone() {
|
|
44262
|
-
return this.done;
|
|
44263
|
-
}
|
|
44264
|
-
ensureStarted() {
|
|
44265
|
-
if (!this.started) {
|
|
44266
|
-
this.emitStart();
|
|
44522
|
+
}
|
|
44523
|
+
if (content.length > 0 && !content.endsWith(`
|
|
44524
|
+
`)) {
|
|
44525
|
+
content += `
|
|
44526
|
+
`;
|
|
44527
|
+
}
|
|
44528
|
+
if (content.trim().length > 0) {
|
|
44529
|
+
content += `
|
|
44530
|
+
`;
|
|
44267
44531
|
}
|
|
44268
44532
|
}
|
|
44269
|
-
|
|
44270
|
-
|
|
44271
|
-
|
|
44272
|
-
}
|
|
44533
|
+
content += `${locusBlock}
|
|
44534
|
+
`;
|
|
44535
|
+
writeFileSync7(gitignorePath, content);
|
|
44273
44536
|
}
|
|
44274
|
-
|
|
44275
|
-
|
|
44276
|
-
|
|
44277
|
-
|
|
44278
|
-
|
|
44279
|
-
|
|
44280
|
-
|
|
44281
|
-
|
|
44282
|
-
|
|
44283
|
-
|
|
44284
|
-
|
|
44285
|
-
const
|
|
44286
|
-
|
|
44287
|
-
|
|
44288
|
-
|
|
44289
|
-
|
|
44290
|
-
|
|
44291
|
-
|
|
44537
|
+
function ensureGitIdentity(projectPath) {
|
|
44538
|
+
const hasName = (() => {
|
|
44539
|
+
try {
|
|
44540
|
+
return execSync2("git config --get user.name", {
|
|
44541
|
+
cwd: projectPath,
|
|
44542
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
44543
|
+
}).toString().trim();
|
|
44544
|
+
} catch {
|
|
44545
|
+
return "";
|
|
44546
|
+
}
|
|
44547
|
+
})();
|
|
44548
|
+
const hasEmail = (() => {
|
|
44549
|
+
try {
|
|
44550
|
+
return execSync2("git config --get user.email", {
|
|
44551
|
+
cwd: projectPath,
|
|
44552
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
44553
|
+
}).toString().trim();
|
|
44554
|
+
} catch {
|
|
44555
|
+
return "";
|
|
44556
|
+
}
|
|
44557
|
+
})();
|
|
44558
|
+
if (!hasName) {
|
|
44559
|
+
execSync2('git config user.name "LocusAgent"', {
|
|
44560
|
+
cwd: projectPath,
|
|
44561
|
+
stdio: "ignore"
|
|
44562
|
+
});
|
|
44292
44563
|
}
|
|
44293
|
-
if (
|
|
44294
|
-
|
|
44564
|
+
if (!hasEmail) {
|
|
44565
|
+
execSync2('git config user.email "agent@locusai.team"', {
|
|
44566
|
+
cwd: projectPath,
|
|
44567
|
+
stdio: "ignore"
|
|
44568
|
+
});
|
|
44295
44569
|
}
|
|
44296
|
-
|
|
44570
|
+
execSync2("git config --global pull.rebase true", {
|
|
44571
|
+
cwd: projectPath,
|
|
44572
|
+
stdio: "ignore"
|
|
44573
|
+
});
|
|
44297
44574
|
}
|
|
44298
44575
|
|
|
44299
|
-
class
|
|
44300
|
-
|
|
44576
|
+
class ConfigManager {
|
|
44577
|
+
projectPath;
|
|
44301
44578
|
constructor(projectPath) {
|
|
44302
|
-
this.
|
|
44579
|
+
this.projectPath = projectPath;
|
|
44303
44580
|
}
|
|
44304
|
-
async
|
|
44305
|
-
const
|
|
44306
|
-
|
|
44307
|
-
|
|
44308
|
-
|
|
44309
|
-
|
|
44310
|
-
|
|
44581
|
+
async init(version2) {
|
|
44582
|
+
const locusConfigDir = join16(this.projectPath, LOCUS_CONFIG.dir);
|
|
44583
|
+
const locusConfigPath = getLocusPath(this.projectPath, "configFile");
|
|
44584
|
+
if (!existsSync16(locusConfigDir)) {
|
|
44585
|
+
mkdirSync7(locusConfigDir, { recursive: true });
|
|
44586
|
+
}
|
|
44587
|
+
const locusSubdirs = [
|
|
44588
|
+
LOCUS_CONFIG.artifactsDir,
|
|
44589
|
+
LOCUS_CONFIG.documentsDir,
|
|
44590
|
+
LOCUS_CONFIG.sessionsDir,
|
|
44591
|
+
LOCUS_CONFIG.reviewsDir,
|
|
44592
|
+
LOCUS_CONFIG.plansDir,
|
|
44593
|
+
LOCUS_CONFIG.discussionsDir
|
|
44594
|
+
];
|
|
44595
|
+
for (const subdir of locusSubdirs) {
|
|
44596
|
+
const subdirPath = join16(locusConfigDir, subdir);
|
|
44597
|
+
if (!existsSync16(subdirPath)) {
|
|
44598
|
+
mkdirSync7(subdirPath, { recursive: true });
|
|
44599
|
+
}
|
|
44600
|
+
}
|
|
44601
|
+
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
44602
|
+
if (!existsSync16(locusMdPath)) {
|
|
44603
|
+
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
44311
44604
|
}
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
|
|
44315
|
-
for (const session2 of sessions.slice(0, 10)) {
|
|
44316
|
-
const shortId = this.getShortId(session2.id);
|
|
44317
|
-
const age = formatRelativeTime(session2.updatedAt);
|
|
44318
|
-
const msgCount = session2.messages.length;
|
|
44319
|
-
const firstUserMsg = session2.messages.find((m) => m.role === "user");
|
|
44320
|
-
const preview = firstUserMsg ? firstUserMsg.content.slice(0, 50).replace(/\n/g, " ") : "(empty session)";
|
|
44321
|
-
console.log(` ${c.cyan(shortId)} ${c.gray("-")} ${preview}${firstUserMsg && firstUserMsg.content.length > 50 ? "..." : ""}`);
|
|
44322
|
-
console.log(` ${c.dim(`${msgCount} messages • ${age}`)}`);
|
|
44323
|
-
console.log();
|
|
44605
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
44606
|
+
if (!existsSync16(learningsMdPath)) {
|
|
44607
|
+
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
44324
44608
|
}
|
|
44325
|
-
if (
|
|
44326
|
-
|
|
44327
|
-
|
|
44609
|
+
if (!existsSync16(locusConfigPath)) {
|
|
44610
|
+
const config2 = {
|
|
44611
|
+
$schema: LOCUS_SCHEMAS.config,
|
|
44612
|
+
version: version2,
|
|
44613
|
+
createdAt: new Date().toISOString(),
|
|
44614
|
+
projectPath: "."
|
|
44615
|
+
};
|
|
44616
|
+
writeFileSync7(locusConfigPath, JSON.stringify(config2, null, 2));
|
|
44328
44617
|
}
|
|
44618
|
+
updateGitignore(this.projectPath);
|
|
44619
|
+
ensureGitIdentity(this.projectPath);
|
|
44329
44620
|
}
|
|
44330
|
-
|
|
44331
|
-
|
|
44332
|
-
|
|
44333
|
-
|
|
44334
|
-
`);
|
|
44335
|
-
console.log(` ${c.dim("Usage: locus exec sessions show <session-id>")}
|
|
44336
|
-
`);
|
|
44337
|
-
return;
|
|
44621
|
+
loadConfig() {
|
|
44622
|
+
const path3 = getLocusPath(this.projectPath, "configFile");
|
|
44623
|
+
if (existsSync16(path3)) {
|
|
44624
|
+
return JSON.parse(readFileSync14(path3, "utf-8"));
|
|
44338
44625
|
}
|
|
44339
|
-
|
|
44340
|
-
|
|
44341
|
-
|
|
44342
|
-
|
|
44343
|
-
|
|
44344
|
-
|
|
44345
|
-
|
|
44346
|
-
return;
|
|
44626
|
+
return null;
|
|
44627
|
+
}
|
|
44628
|
+
updateVersion(version2) {
|
|
44629
|
+
const config2 = this.loadConfig();
|
|
44630
|
+
if (config2 && config2.version !== version2) {
|
|
44631
|
+
config2.version = version2;
|
|
44632
|
+
this.saveConfig(config2);
|
|
44347
44633
|
}
|
|
44348
|
-
|
|
44349
|
-
|
|
44350
|
-
|
|
44351
|
-
|
|
44352
|
-
|
|
44353
|
-
|
|
44354
|
-
|
|
44355
|
-
|
|
44356
|
-
|
|
44634
|
+
}
|
|
44635
|
+
async reinit(version2) {
|
|
44636
|
+
const result = {
|
|
44637
|
+
versionUpdated: false,
|
|
44638
|
+
previousVersion: null,
|
|
44639
|
+
directoriesCreated: [],
|
|
44640
|
+
gitignoreUpdated: false
|
|
44641
|
+
};
|
|
44642
|
+
const config2 = this.loadConfig();
|
|
44643
|
+
if (config2) {
|
|
44644
|
+
result.previousVersion = config2.version;
|
|
44645
|
+
const needsSchemaUpdate = config2.$schema !== LOCUS_SCHEMAS.config;
|
|
44646
|
+
if (config2.version !== version2) {
|
|
44647
|
+
config2.version = version2;
|
|
44648
|
+
result.versionUpdated = true;
|
|
44649
|
+
}
|
|
44650
|
+
if (result.versionUpdated || needsSchemaUpdate) {
|
|
44651
|
+
this.saveConfig(config2);
|
|
44652
|
+
}
|
|
44357
44653
|
}
|
|
44358
|
-
|
|
44359
|
-
|
|
44360
|
-
|
|
44361
|
-
const
|
|
44362
|
-
|
|
44363
|
-
|
|
44364
|
-
|
|
44365
|
-
|
|
44366
|
-
for (const line of lines) {
|
|
44367
|
-
console.log(` ${line}`);
|
|
44654
|
+
const settingsPath = join16(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
44655
|
+
if (existsSync16(settingsPath)) {
|
|
44656
|
+
const raw = readFileSync14(settingsPath, "utf-8");
|
|
44657
|
+
const settings = JSON.parse(raw);
|
|
44658
|
+
if (settings.$schema !== LOCUS_SCHEMAS.settings) {
|
|
44659
|
+
const { $schema: _2, ...rest } = settings;
|
|
44660
|
+
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
44661
|
+
writeFileSync7(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
44368
44662
|
}
|
|
44369
|
-
console.log();
|
|
44370
44663
|
}
|
|
44371
|
-
|
|
44372
|
-
|
|
44373
|
-
|
|
44374
|
-
|
|
44375
|
-
|
|
44376
|
-
`);
|
|
44377
|
-
console.log(` ${c.dim("Usage: locus exec sessions delete <session-id>")}
|
|
44378
|
-
`);
|
|
44379
|
-
return;
|
|
44664
|
+
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
44665
|
+
const locusMdExisted = existsSync16(locusMdPath);
|
|
44666
|
+
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
44667
|
+
if (!locusMdExisted) {
|
|
44668
|
+
result.directoriesCreated.push(".locus/LOCUS.md");
|
|
44380
44669
|
}
|
|
44381
|
-
const
|
|
44382
|
-
|
|
44383
|
-
|
|
44384
|
-
|
|
44385
|
-
|
|
44386
|
-
|
|
44670
|
+
const locusSubdirs = [
|
|
44671
|
+
LOCUS_CONFIG.artifactsDir,
|
|
44672
|
+
LOCUS_CONFIG.documentsDir,
|
|
44673
|
+
LOCUS_CONFIG.sessionsDir,
|
|
44674
|
+
LOCUS_CONFIG.reviewsDir,
|
|
44675
|
+
LOCUS_CONFIG.plansDir,
|
|
44676
|
+
LOCUS_CONFIG.discussionsDir
|
|
44677
|
+
];
|
|
44678
|
+
const locusConfigDir = join16(this.projectPath, LOCUS_CONFIG.dir);
|
|
44679
|
+
for (const subdir of locusSubdirs) {
|
|
44680
|
+
const subdirPath = join16(locusConfigDir, subdir);
|
|
44681
|
+
if (!existsSync16(subdirPath)) {
|
|
44682
|
+
mkdirSync7(subdirPath, { recursive: true });
|
|
44683
|
+
result.directoriesCreated.push(`.locus/${subdir}`);
|
|
44684
|
+
}
|
|
44387
44685
|
}
|
|
44388
|
-
const
|
|
44389
|
-
if (
|
|
44390
|
-
|
|
44391
|
-
|
|
44392
|
-
`);
|
|
44393
|
-
} else {
|
|
44394
|
-
console.error(`
|
|
44395
|
-
${c.error("Error:")} Failed to delete session
|
|
44396
|
-
`);
|
|
44686
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
44687
|
+
if (!existsSync16(learningsMdPath)) {
|
|
44688
|
+
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
44689
|
+
result.directoriesCreated.push(".locus/LEARNINGS.md");
|
|
44397
44690
|
}
|
|
44398
|
-
|
|
44399
|
-
|
|
44400
|
-
|
|
44401
|
-
|
|
44402
|
-
|
|
44403
|
-
|
|
44404
|
-
`);
|
|
44405
|
-
return;
|
|
44691
|
+
const gitignorePath = join16(this.projectPath, ".gitignore");
|
|
44692
|
+
const gitignoreBefore = existsSync16(gitignorePath) ? readFileSync14(gitignorePath, "utf-8") : "";
|
|
44693
|
+
updateGitignore(this.projectPath);
|
|
44694
|
+
const gitignoreAfter = readFileSync14(gitignorePath, "utf-8");
|
|
44695
|
+
if (gitignoreBefore !== gitignoreAfter) {
|
|
44696
|
+
result.gitignoreUpdated = true;
|
|
44406
44697
|
}
|
|
44407
|
-
|
|
44408
|
-
|
|
44409
|
-
${c.success("✔")} Cleared ${deleted} exec session${deleted === 1 ? "" : "s"}
|
|
44410
|
-
`);
|
|
44698
|
+
ensureGitIdentity(this.projectPath);
|
|
44699
|
+
return result;
|
|
44411
44700
|
}
|
|
44412
|
-
|
|
44413
|
-
const
|
|
44414
|
-
|
|
44415
|
-
|
|
44416
|
-
|
|
44417
|
-
return sessionId.slice(0, 8);
|
|
44701
|
+
saveConfig(config2) {
|
|
44702
|
+
const { $schema: _2, ...rest } = config2;
|
|
44703
|
+
const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
|
|
44704
|
+
const path3 = getLocusPath(this.projectPath, "configFile");
|
|
44705
|
+
writeFileSync7(path3, JSON.stringify(ordered, null, 2));
|
|
44418
44706
|
}
|
|
44419
44707
|
}
|
|
44420
|
-
function showSessionsHelp() {
|
|
44421
|
-
console.log(`
|
|
44422
|
-
${c.primary("Session Commands")}
|
|
44423
|
-
|
|
44424
|
-
${c.success("list")} List recent exec sessions
|
|
44425
|
-
${c.success("show")} ${c.dim("<id>")} Show all messages in a session
|
|
44426
|
-
${c.success("delete")} ${c.dim("<id>")} Delete a specific session
|
|
44427
|
-
${c.success("clear")} Clear all exec sessions
|
|
44428
44708
|
|
|
44429
|
-
|
|
44430
|
-
|
|
44431
|
-
|
|
44432
|
-
${c.dim("$")} locus exec sessions delete e7f3a2b1
|
|
44433
|
-
${c.dim("$")} locus exec sessions clear
|
|
44709
|
+
// src/commands/docs.ts
|
|
44710
|
+
init_settings_manager();
|
|
44711
|
+
init_utils3();
|
|
44434
44712
|
|
|
44435
|
-
|
|
44436
|
-
|
|
44437
|
-
}
|
|
44713
|
+
// src/workspace-resolver.ts
|
|
44714
|
+
init_index_node();
|
|
44438
44715
|
|
|
44439
|
-
|
|
44440
|
-
|
|
44441
|
-
|
|
44442
|
-
|
|
44443
|
-
options: {
|
|
44444
|
-
model: { type: "string" },
|
|
44445
|
-
provider: { type: "string" },
|
|
44446
|
-
"reasoning-effort": { type: "string" },
|
|
44447
|
-
dir: { type: "string" },
|
|
44448
|
-
"no-stream": { type: "boolean" },
|
|
44449
|
-
"no-status": { type: "boolean" },
|
|
44450
|
-
interactive: { type: "boolean", short: "i" },
|
|
44451
|
-
session: { type: "string", short: "s" },
|
|
44452
|
-
"session-id": { type: "string" },
|
|
44453
|
-
"json-stream": { type: "boolean" }
|
|
44454
|
-
},
|
|
44455
|
-
strict: false
|
|
44456
|
-
});
|
|
44457
|
-
const jsonStream = values["json-stream"];
|
|
44458
|
-
const projectPath = values.dir || process.cwd();
|
|
44459
|
-
if (jsonStream) {
|
|
44460
|
-
await execJsonStream(values, positionals, projectPath);
|
|
44461
|
-
return;
|
|
44716
|
+
class WorkspaceResolver {
|
|
44717
|
+
options;
|
|
44718
|
+
constructor(options) {
|
|
44719
|
+
this.options = options;
|
|
44462
44720
|
}
|
|
44463
|
-
|
|
44464
|
-
|
|
44465
|
-
|
|
44466
|
-
|
|
44467
|
-
|
|
44468
|
-
|
|
44469
|
-
|
|
44470
|
-
|
|
44471
|
-
|
|
44472
|
-
|
|
44473
|
-
|
|
44474
|
-
|
|
44475
|
-
|
|
44476
|
-
|
|
44477
|
-
|
|
44478
|
-
|
|
44479
|
-
|
|
44480
|
-
|
|
44481
|
-
default:
|
|
44482
|
-
showSessionsHelp();
|
|
44483
|
-
return;
|
|
44721
|
+
async resolve() {
|
|
44722
|
+
if (this.options.workspaceId) {
|
|
44723
|
+
return this.options.workspaceId;
|
|
44724
|
+
}
|
|
44725
|
+
try {
|
|
44726
|
+
console.log(c.dim("ℹ Resolving workspace from API key..."));
|
|
44727
|
+
const client = new LocusClient({
|
|
44728
|
+
baseUrl: this.options.apiBase,
|
|
44729
|
+
token: this.options.apiKey
|
|
44730
|
+
});
|
|
44731
|
+
const info = await client.auth.getApiKeyInfo();
|
|
44732
|
+
if (info.workspaceId) {
|
|
44733
|
+
console.log(c.success(`✓ Resolved workspace: ${info.workspaceId}`));
|
|
44734
|
+
return info.workspaceId;
|
|
44735
|
+
}
|
|
44736
|
+
throw new Error("API key is not associated with a workspace. Please specify --workspace.");
|
|
44737
|
+
} catch (error48) {
|
|
44738
|
+
throw new Error(`Error resolving workspace: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
44484
44739
|
}
|
|
44485
44740
|
}
|
|
44486
|
-
|
|
44487
|
-
|
|
44488
|
-
|
|
44489
|
-
|
|
44490
|
-
const
|
|
44491
|
-
|
|
44492
|
-
|
|
44493
|
-
|
|
44494
|
-
|
|
44495
|
-
|
|
44496
|
-
|
|
44497
|
-
|
|
44498
|
-
|
|
44499
|
-
|
|
44741
|
+
}
|
|
44742
|
+
|
|
44743
|
+
// src/commands/docs.ts
|
|
44744
|
+
async function docsCommand(args) {
|
|
44745
|
+
const subcommand = args[0];
|
|
44746
|
+
const subArgs = args.slice(1);
|
|
44747
|
+
switch (subcommand) {
|
|
44748
|
+
case "sync":
|
|
44749
|
+
await docsSyncCommand(subArgs);
|
|
44750
|
+
break;
|
|
44751
|
+
default:
|
|
44752
|
+
showDocsHelp();
|
|
44753
|
+
break;
|
|
44754
|
+
}
|
|
44755
|
+
}
|
|
44756
|
+
async function docsSyncCommand(args) {
|
|
44757
|
+
const { values } = parseArgs4({
|
|
44758
|
+
args,
|
|
44759
|
+
options: {
|
|
44760
|
+
"api-key": { type: "string" },
|
|
44761
|
+
"api-url": { type: "string" },
|
|
44762
|
+
workspace: { type: "string" },
|
|
44763
|
+
dir: { type: "string" },
|
|
44764
|
+
help: { type: "boolean" }
|
|
44765
|
+
},
|
|
44766
|
+
strict: false
|
|
44767
|
+
});
|
|
44768
|
+
if (values.help) {
|
|
44769
|
+
showDocsSyncHelp();
|
|
44500
44770
|
return;
|
|
44501
44771
|
}
|
|
44502
|
-
const
|
|
44503
|
-
|
|
44504
|
-
|
|
44772
|
+
const projectPath = values.dir || process.cwd();
|
|
44773
|
+
requireInitialization(projectPath, "docs sync");
|
|
44774
|
+
const configManager = new ConfigManager(projectPath);
|
|
44775
|
+
configManager.updateVersion(VERSION2);
|
|
44776
|
+
const settingsManager = new SettingsManager(projectPath);
|
|
44777
|
+
const settings = settingsManager.load();
|
|
44778
|
+
const apiKey = values["api-key"] || settings.apiKey;
|
|
44779
|
+
if (!apiKey) {
|
|
44780
|
+
console.error(`
|
|
44781
|
+
${c.error("✖")} ${c.red("API key is required")}
|
|
44782
|
+
` + ` ${c.dim(`Configure with: locus config setup --api-key <key>
|
|
44783
|
+
Or pass --api-key flag`)}
|
|
44784
|
+
`);
|
|
44505
44785
|
process.exit(1);
|
|
44506
44786
|
}
|
|
44507
|
-
const
|
|
44508
|
-
const
|
|
44509
|
-
|
|
44510
|
-
|
|
44511
|
-
|
|
44512
|
-
reasoningEffort
|
|
44787
|
+
const apiBase = values["api-url"] || settings.apiUrl || "https://api.locusai.dev/api";
|
|
44788
|
+
const resolver = new WorkspaceResolver({
|
|
44789
|
+
apiKey,
|
|
44790
|
+
apiBase,
|
|
44791
|
+
workspaceId: values.workspace
|
|
44513
44792
|
});
|
|
44514
|
-
|
|
44515
|
-
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
44516
|
-
console.log("");
|
|
44517
|
-
console.log(`${c.primary("\uD83D\uDE80")} ${c.bold("Executing prompt with repository context...")}`);
|
|
44518
|
-
console.log("");
|
|
44519
|
-
let responseContent = "";
|
|
44793
|
+
let workspaceId;
|
|
44520
44794
|
try {
|
|
44521
|
-
|
|
44522
|
-
const renderer = new ProgressRenderer;
|
|
44523
|
-
const statsTracker = new ExecutionStatsTracker;
|
|
44524
|
-
const stream4 = aiRunner.runStream(fullPrompt);
|
|
44525
|
-
renderer.showThinkingStarted();
|
|
44526
|
-
for await (const chunk of stream4) {
|
|
44527
|
-
switch (chunk.type) {
|
|
44528
|
-
case "text_delta":
|
|
44529
|
-
renderer.renderTextDelta(chunk.content);
|
|
44530
|
-
responseContent += chunk.content;
|
|
44531
|
-
break;
|
|
44532
|
-
case "tool_use":
|
|
44533
|
-
statsTracker.toolStarted(chunk.tool, chunk.id);
|
|
44534
|
-
renderer.showToolStarted(chunk.tool, chunk.id);
|
|
44535
|
-
break;
|
|
44536
|
-
case "thinking":
|
|
44537
|
-
renderer.showThinkingStarted();
|
|
44538
|
-
break;
|
|
44539
|
-
case "tool_result":
|
|
44540
|
-
if (chunk.success) {
|
|
44541
|
-
statsTracker.toolCompleted(chunk.tool, chunk.id);
|
|
44542
|
-
renderer.showToolCompleted(chunk.tool, undefined, chunk.id);
|
|
44543
|
-
} else {
|
|
44544
|
-
statsTracker.toolFailed(chunk.tool, chunk.error ?? "Unknown error", chunk.id);
|
|
44545
|
-
renderer.showToolFailed(chunk.tool, chunk.error ?? "Unknown error", chunk.id);
|
|
44546
|
-
}
|
|
44547
|
-
break;
|
|
44548
|
-
case "result":
|
|
44549
|
-
break;
|
|
44550
|
-
case "error": {
|
|
44551
|
-
statsTracker.setError(chunk.error);
|
|
44552
|
-
renderer.renderError(chunk.error);
|
|
44553
|
-
renderer.finalize();
|
|
44554
|
-
const errorStats = statsTracker.finalize();
|
|
44555
|
-
renderer.showSummary(errorStats);
|
|
44556
|
-
console.error(`
|
|
44557
|
-
${c.error("✖")} ${c.error("Execution failed!")}
|
|
44558
|
-
`);
|
|
44559
|
-
process.exit(1);
|
|
44560
|
-
}
|
|
44561
|
-
}
|
|
44562
|
-
}
|
|
44563
|
-
renderer.finalize();
|
|
44564
|
-
const stats = statsTracker.finalize();
|
|
44565
|
-
renderer.showSummary(stats);
|
|
44566
|
-
} else {
|
|
44567
|
-
responseContent = await aiRunner.run(fullPrompt);
|
|
44568
|
-
console.log(responseContent);
|
|
44569
|
-
}
|
|
44570
|
-
console.log(`
|
|
44571
|
-
${c.success("✔")} ${c.success("Execution finished!")}
|
|
44572
|
-
`);
|
|
44795
|
+
workspaceId = await resolver.resolve();
|
|
44573
44796
|
} catch (error48) {
|
|
44574
44797
|
console.error(`
|
|
44575
|
-
${c.error("✖")} ${c.
|
|
44798
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
44576
44799
|
`);
|
|
44577
44800
|
process.exit(1);
|
|
44578
44801
|
}
|
|
44579
|
-
|
|
44580
|
-
|
|
44581
|
-
|
|
44582
|
-
const execSettings = new SettingsManager(projectPath).load();
|
|
44583
|
-
const provider = resolveProvider3(values.provider || execSettings.provider);
|
|
44584
|
-
const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
|
|
44585
|
-
const renderer = new JsonStreamRenderer({
|
|
44586
|
-
sessionId,
|
|
44587
|
-
command: "exec",
|
|
44588
|
-
model,
|
|
44589
|
-
provider,
|
|
44590
|
-
cwd: projectPath
|
|
44802
|
+
const client = new LocusClient({
|
|
44803
|
+
baseUrl: apiBase,
|
|
44804
|
+
token: apiKey
|
|
44591
44805
|
});
|
|
44592
|
-
const
|
|
44593
|
-
|
|
44594
|
-
|
|
44806
|
+
const fetcher = new DocumentFetcher({
|
|
44807
|
+
client,
|
|
44808
|
+
workspaceId,
|
|
44809
|
+
projectPath,
|
|
44810
|
+
log: (message, level) => {
|
|
44811
|
+
if (level === "error") {
|
|
44812
|
+
console.log(` ${c.error("✖")} ${message}`);
|
|
44813
|
+
return;
|
|
44814
|
+
}
|
|
44815
|
+
if (level === "warn") {
|
|
44816
|
+
console.log(` ${c.warning("!")} ${message}`);
|
|
44817
|
+
return;
|
|
44818
|
+
}
|
|
44819
|
+
if (level === "success") {
|
|
44820
|
+
console.log(` ${c.success("✔")} ${message}`);
|
|
44821
|
+
return;
|
|
44822
|
+
}
|
|
44823
|
+
console.log(` ${c.info("●")} ${message}`);
|
|
44595
44824
|
}
|
|
44596
|
-
|
|
44597
|
-
|
|
44598
|
-
|
|
44599
|
-
|
|
44825
|
+
});
|
|
44826
|
+
console.log(`
|
|
44827
|
+
${c.info("●")} ${c.bold("Syncing docs from API...")}
|
|
44828
|
+
`);
|
|
44600
44829
|
try {
|
|
44601
|
-
|
|
44602
|
-
|
|
44603
|
-
|
|
44604
|
-
|
|
44605
|
-
process.exit(1);
|
|
44606
|
-
}
|
|
44607
|
-
const promptInput = positionals.join(" ");
|
|
44608
|
-
if (!promptInput) {
|
|
44609
|
-
renderer.emitFatalError("UNKNOWN", 'Prompt is required. Usage: locus exec --json-stream "your prompt"');
|
|
44610
|
-
process.exit(1);
|
|
44611
|
-
}
|
|
44612
|
-
renderer.emitStart();
|
|
44613
|
-
renderer.emitStatus("running", "Building prompt context");
|
|
44614
|
-
const aiRunner = createAiRunner(provider, {
|
|
44615
|
-
projectPath,
|
|
44616
|
-
model
|
|
44617
|
-
});
|
|
44618
|
-
const builder = new PromptBuilder(projectPath);
|
|
44619
|
-
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
44620
|
-
renderer.emitStatus("streaming", "Streaming AI response");
|
|
44621
|
-
const stream4 = aiRunner.runStream(fullPrompt);
|
|
44622
|
-
for await (const chunk of stream4) {
|
|
44623
|
-
renderer.handleChunk(chunk);
|
|
44624
|
-
}
|
|
44625
|
-
renderer.emitDone(0);
|
|
44830
|
+
await fetcher.fetch();
|
|
44831
|
+
console.log(`
|
|
44832
|
+
${c.success("✔")} ${c.success("Docs sync complete.")} ${c.dim("Local docs: .locus/documents")}
|
|
44833
|
+
`);
|
|
44626
44834
|
} catch (error48) {
|
|
44627
|
-
|
|
44628
|
-
|
|
44629
|
-
|
|
44630
|
-
}
|
|
44835
|
+
console.error(`
|
|
44836
|
+
${c.error("✖")} ${c.red(`Docs sync failed: ${error48 instanceof Error ? error48.message : String(error48)}`)}
|
|
44837
|
+
`);
|
|
44631
44838
|
process.exit(1);
|
|
44632
44839
|
}
|
|
44633
44840
|
}
|
|
44841
|
+
function showDocsHelp() {
|
|
44842
|
+
console.log(`
|
|
44843
|
+
${c.header(" DOCS ")}
|
|
44844
|
+
${c.primary("locus docs")} ${c.dim("<command> [options]")}
|
|
44845
|
+
|
|
44846
|
+
${c.header(" COMMANDS ")}
|
|
44847
|
+
${c.success("sync")} Sync workspace docs from API to .locus/documents
|
|
44848
|
+
|
|
44849
|
+
${c.header(" EXAMPLES ")}
|
|
44850
|
+
${c.dim("$")} ${c.primary("locus docs sync")}
|
|
44851
|
+
${c.dim("$")} ${c.primary("locus docs sync --workspace ws_123")}
|
|
44852
|
+
`);
|
|
44853
|
+
}
|
|
44854
|
+
function showDocsSyncHelp() {
|
|
44855
|
+
console.log(`
|
|
44856
|
+
${c.header(" DOCS SYNC ")}
|
|
44857
|
+
${c.primary("locus docs sync")} ${c.dim("[options]")}
|
|
44858
|
+
|
|
44859
|
+
${c.header(" OPTIONS ")}
|
|
44860
|
+
${c.secondary("--api-key")} <key> API key override (reads from settings.json)
|
|
44861
|
+
${c.secondary("--api-url")} <url> API base URL (default: https://api.locusai.dev/api)
|
|
44862
|
+
${c.secondary("--workspace")} <id> Workspace ID (optional if persisted or resolvable)
|
|
44863
|
+
${c.secondary("--dir")} <path> Project directory (default: current)
|
|
44864
|
+
${c.secondary("--help")} Show docs sync help
|
|
44865
|
+
|
|
44866
|
+
${c.header(" EXAMPLES ")}
|
|
44867
|
+
${c.dim("$")} ${c.primary("locus docs sync")}
|
|
44868
|
+
${c.dim("$")} ${c.primary("locus docs sync --workspace ws_123")}
|
|
44869
|
+
`);
|
|
44870
|
+
}
|
|
44871
|
+
|
|
44872
|
+
// src/commands/index.ts
|
|
44873
|
+
init_exec2();
|
|
44874
|
+
init_exec_sessions();
|
|
44875
|
+
|
|
44634
44876
|
// src/commands/help.ts
|
|
44635
44877
|
init_index_node();
|
|
44636
44878
|
function showHelp2() {
|
|
@@ -44669,6 +44911,9 @@ function showHelp2() {
|
|
|
44669
44911
|
${c.dim("sessions show <id> Show session messages")}
|
|
44670
44912
|
${c.dim("sessions delete <id> Delete a session")}
|
|
44671
44913
|
${c.dim("sessions clear Clear all sessions")}
|
|
44914
|
+
${c.success("artifacts")} List and manage knowledge artifacts
|
|
44915
|
+
${c.dim("show <name> Show artifact content")}
|
|
44916
|
+
${c.dim("plan <name> Convert artifact to a plan")}
|
|
44672
44917
|
${c.success("version")} Show installed package versions
|
|
44673
44918
|
${c.success("upgrade")} Update CLI and Telegram to the latest version
|
|
44674
44919
|
|
|
@@ -44692,13 +44937,15 @@ function showHelp2() {
|
|
|
44692
44937
|
${c.dim("$")} ${c.primary("locus telegram setup")}
|
|
44693
44938
|
${c.dim("$")} ${c.primary('locus discuss "how should we design the auth system?"')}
|
|
44694
44939
|
${c.dim("$")} ${c.primary("locus exec sessions list")}
|
|
44940
|
+
${c.dim("$")} ${c.primary("locus artifacts")}
|
|
44941
|
+
${c.dim("$")} ${c.primary("locus artifacts show reduce-cli-terminal-output")}
|
|
44695
44942
|
|
|
44696
44943
|
For more information, visit: ${c.underline("https://docs.locusai.dev")}
|
|
44697
44944
|
`);
|
|
44698
44945
|
}
|
|
44699
44946
|
// src/commands/index-codebase.ts
|
|
44700
44947
|
init_index_node();
|
|
44701
|
-
import { parseArgs as
|
|
44948
|
+
import { parseArgs as parseArgs5 } from "node:util";
|
|
44702
44949
|
|
|
44703
44950
|
// src/tree-summarizer.ts
|
|
44704
44951
|
init_index_node();
|
|
@@ -44725,8 +44972,9 @@ ${tree}`;
|
|
|
44725
44972
|
}
|
|
44726
44973
|
|
|
44727
44974
|
// src/commands/index-codebase.ts
|
|
44975
|
+
init_utils3();
|
|
44728
44976
|
async function indexCommand(args) {
|
|
44729
|
-
const { values } =
|
|
44977
|
+
const { values } = parseArgs5({
|
|
44730
44978
|
args,
|
|
44731
44979
|
options: {
|
|
44732
44980
|
dir: { type: "string" },
|
|
@@ -44765,6 +45013,7 @@ async function indexCommand(args) {
|
|
|
44765
45013
|
}
|
|
44766
45014
|
// src/commands/init.ts
|
|
44767
45015
|
init_index_node();
|
|
45016
|
+
init_utils3();
|
|
44768
45017
|
async function initCommand() {
|
|
44769
45018
|
const projectPath = process.cwd();
|
|
44770
45019
|
const configManager = new ConfigManager(projectPath);
|
|
@@ -44824,9 +45073,11 @@ async function initCommand() {
|
|
|
44824
45073
|
}
|
|
44825
45074
|
// src/commands/plan.ts
|
|
44826
45075
|
init_index_node();
|
|
44827
|
-
import { existsSync as
|
|
44828
|
-
import { join as
|
|
44829
|
-
import { parseArgs as
|
|
45076
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync5 } from "node:fs";
|
|
45077
|
+
import { join as join17 } from "node:path";
|
|
45078
|
+
import { parseArgs as parseArgs6 } from "node:util";
|
|
45079
|
+
init_settings_manager();
|
|
45080
|
+
init_utils3();
|
|
44830
45081
|
function normalizePlanIdArgs(args) {
|
|
44831
45082
|
const planIdFlags = new Set(["--approve", "--reject", "--cancel", "--show"]);
|
|
44832
45083
|
const result = [];
|
|
@@ -44845,7 +45096,7 @@ function normalizePlanIdArgs(args) {
|
|
|
44845
45096
|
}
|
|
44846
45097
|
async function planCommand(args) {
|
|
44847
45098
|
const normalizedArgs = normalizePlanIdArgs(args);
|
|
44848
|
-
const { values, positionals } =
|
|
45099
|
+
const { values, positionals } = parseArgs6({
|
|
44849
45100
|
args: normalizedArgs,
|
|
44850
45101
|
options: {
|
|
44851
45102
|
approve: { type: "string" },
|
|
@@ -44932,8 +45183,8 @@ async function planCommand(args) {
|
|
|
44932
45183
|
try {
|
|
44933
45184
|
const result = await meeting.run(directive, feedback);
|
|
44934
45185
|
planManager.save(result.plan);
|
|
44935
|
-
const tempFile =
|
|
44936
|
-
if (
|
|
45186
|
+
const tempFile = join17(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
|
|
45187
|
+
if (existsSync17(tempFile)) {
|
|
44937
45188
|
unlinkSync5(tempFile);
|
|
44938
45189
|
}
|
|
44939
45190
|
console.log(`
|
|
@@ -45130,9 +45381,11 @@ function showPlanHelp() {
|
|
|
45130
45381
|
}
|
|
45131
45382
|
// src/commands/review.ts
|
|
45132
45383
|
init_index_node();
|
|
45133
|
-
import { existsSync as
|
|
45134
|
-
import { join as
|
|
45135
|
-
import { parseArgs as
|
|
45384
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
45385
|
+
import { join as join18 } from "node:path";
|
|
45386
|
+
import { parseArgs as parseArgs7 } from "node:util";
|
|
45387
|
+
init_settings_manager();
|
|
45388
|
+
init_utils3();
|
|
45136
45389
|
async function reviewCommand(args) {
|
|
45137
45390
|
const subcommand = args[0];
|
|
45138
45391
|
if (subcommand === "local") {
|
|
@@ -45141,7 +45394,7 @@ async function reviewCommand(args) {
|
|
|
45141
45394
|
return reviewPrsCommand(args);
|
|
45142
45395
|
}
|
|
45143
45396
|
async function reviewPrsCommand(args) {
|
|
45144
|
-
const { values } =
|
|
45397
|
+
const { values } = parseArgs7({
|
|
45145
45398
|
args,
|
|
45146
45399
|
options: {
|
|
45147
45400
|
"api-key": { type: "string" },
|
|
@@ -45227,7 +45480,7 @@ ${c.info("Received shutdown signal. Stopping reviewer...")}`);
|
|
|
45227
45480
|
await reviewer.run();
|
|
45228
45481
|
}
|
|
45229
45482
|
async function reviewLocalCommand(args) {
|
|
45230
|
-
const { values } =
|
|
45483
|
+
const { values } = parseArgs7({
|
|
45231
45484
|
args,
|
|
45232
45485
|
options: {
|
|
45233
45486
|
model: { type: "string" },
|
|
@@ -45270,12 +45523,12 @@ async function reviewLocalCommand(args) {
|
|
|
45270
45523
|
`);
|
|
45271
45524
|
return;
|
|
45272
45525
|
}
|
|
45273
|
-
const reviewsDir =
|
|
45274
|
-
if (!
|
|
45526
|
+
const reviewsDir = join18(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
|
|
45527
|
+
if (!existsSync18(reviewsDir)) {
|
|
45275
45528
|
mkdirSync8(reviewsDir, { recursive: true });
|
|
45276
45529
|
}
|
|
45277
45530
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
45278
|
-
const reportPath =
|
|
45531
|
+
const reportPath = join18(reviewsDir, `review-${timestamp}.md`);
|
|
45279
45532
|
writeFileSync8(reportPath, report, "utf-8");
|
|
45280
45533
|
console.log(`
|
|
45281
45534
|
${c.success("✔")} ${c.success("Review complete!")}`);
|
|
@@ -45284,9 +45537,11 @@ async function reviewLocalCommand(args) {
|
|
|
45284
45537
|
}
|
|
45285
45538
|
// src/commands/run.ts
|
|
45286
45539
|
init_index_node();
|
|
45287
|
-
import { parseArgs as
|
|
45540
|
+
import { parseArgs as parseArgs8 } from "node:util";
|
|
45541
|
+
init_settings_manager();
|
|
45542
|
+
init_utils3();
|
|
45288
45543
|
async function runCommand(args) {
|
|
45289
|
-
const { values } =
|
|
45544
|
+
const { values } = parseArgs8({
|
|
45290
45545
|
args,
|
|
45291
45546
|
options: {
|
|
45292
45547
|
"api-key": { type: "string" },
|
|
@@ -45367,9 +45622,10 @@ ${c.info(`Received ${signal}. Stopping agent and cleaning up...`)}`);
|
|
|
45367
45622
|
}
|
|
45368
45623
|
// src/commands/telegram.ts
|
|
45369
45624
|
init_index_node();
|
|
45625
|
+
init_settings_manager();
|
|
45370
45626
|
import { spawn as spawn4 } from "node:child_process";
|
|
45371
|
-
import { existsSync as
|
|
45372
|
-
import { join as
|
|
45627
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
45628
|
+
import { join as join19 } from "node:path";
|
|
45373
45629
|
import { createInterface as createInterface4 } from "node:readline";
|
|
45374
45630
|
function ask2(question) {
|
|
45375
45631
|
const rl = createInterface4({
|
|
@@ -45603,8 +45859,8 @@ function runBotCommand(projectPath) {
|
|
|
45603
45859
|
`);
|
|
45604
45860
|
process.exit(1);
|
|
45605
45861
|
}
|
|
45606
|
-
const monorepoTelegramEntry =
|
|
45607
|
-
const isMonorepo =
|
|
45862
|
+
const monorepoTelegramEntry = join19(projectPath, "packages/telegram/src/index.ts");
|
|
45863
|
+
const isMonorepo = existsSync19(monorepoTelegramEntry);
|
|
45608
45864
|
let cmd;
|
|
45609
45865
|
let args;
|
|
45610
45866
|
if (isMonorepo) {
|
|
@@ -45733,6 +45989,7 @@ async function upgradeCommand() {
|
|
|
45733
45989
|
}
|
|
45734
45990
|
// src/commands/version.ts
|
|
45735
45991
|
init_index_node();
|
|
45992
|
+
init_utils3();
|
|
45736
45993
|
import { execSync as execSync4 } from "node:child_process";
|
|
45737
45994
|
function getTelegramVersion() {
|
|
45738
45995
|
try {
|
|
@@ -45759,6 +46016,7 @@ function versionCommand() {
|
|
|
45759
46016
|
console.log("");
|
|
45760
46017
|
}
|
|
45761
46018
|
// src/cli.ts
|
|
46019
|
+
init_utils3();
|
|
45762
46020
|
var isJsonStream = process.argv.includes("--json-stream");
|
|
45763
46021
|
async function main() {
|
|
45764
46022
|
const command = process.argv[2];
|
|
@@ -45783,6 +46041,9 @@ async function main() {
|
|
|
45783
46041
|
case "exec":
|
|
45784
46042
|
await execCommand(args);
|
|
45785
46043
|
break;
|
|
46044
|
+
case "artifacts":
|
|
46045
|
+
await artifactsCommand(args);
|
|
46046
|
+
break;
|
|
45786
46047
|
case "discuss":
|
|
45787
46048
|
await discussCommand(args);
|
|
45788
46049
|
break;
|