@locusai/cli 0.13.0 → 0.13.2
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/agent/worker.js +302 -12
- package/bin/locus.js +1336 -326
- package/package.json +2 -2
package/bin/locus.js
CHANGED
|
@@ -6393,7 +6393,8 @@ var init_config = __esm(() => {
|
|
|
6393
6393
|
documentsDir: "documents",
|
|
6394
6394
|
sessionsDir: "sessions",
|
|
6395
6395
|
reviewsDir: "reviews",
|
|
6396
|
-
plansDir: "plans"
|
|
6396
|
+
plansDir: "plans",
|
|
6397
|
+
discussionsDir: "discussions"
|
|
6397
6398
|
};
|
|
6398
6399
|
LOCUS_GITIGNORE_PATTERNS = [
|
|
6399
6400
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
@@ -6408,6 +6409,9 @@ var init_config = __esm(() => {
|
|
|
6408
6409
|
"# Locus AI - Plans (generated per task)",
|
|
6409
6410
|
".locus/plans/",
|
|
6410
6411
|
"",
|
|
6412
|
+
"# Locus AI - Discussions (AI discussion sessions)",
|
|
6413
|
+
".locus/discussions/",
|
|
6414
|
+
"",
|
|
6411
6415
|
"# Locus AI - Settings (contains API key, telegram config, etc.)",
|
|
6412
6416
|
".locus/settings.json",
|
|
6413
6417
|
"",
|
|
@@ -38244,7 +38248,8 @@ var init_session = __esm(() => {
|
|
|
38244
38248
|
model: exports_external.string().optional(),
|
|
38245
38249
|
createdAt: exports_external.number(),
|
|
38246
38250
|
updatedAt: exports_external.number(),
|
|
38247
|
-
title: exports_external.string().optional()
|
|
38251
|
+
title: exports_external.string().optional(),
|
|
38252
|
+
prompt: exports_external.string().optional()
|
|
38248
38253
|
});
|
|
38249
38254
|
SessionSummarySchema = exports_external.object({
|
|
38250
38255
|
sessionId: exports_external.string(),
|
|
@@ -38611,6 +38616,39 @@ var init_workspaces = __esm(() => {
|
|
|
38611
38616
|
};
|
|
38612
38617
|
});
|
|
38613
38618
|
|
|
38619
|
+
// ../sdk/src/discussion/discussion-types.ts
|
|
38620
|
+
var DiscussionMessageSchema, DiscussionInsightSchema, DiscussionSchema;
|
|
38621
|
+
var init_discussion_types = __esm(() => {
|
|
38622
|
+
init_zod();
|
|
38623
|
+
DiscussionMessageSchema = exports_external.object({
|
|
38624
|
+
role: exports_external.enum(["user", "assistant"]),
|
|
38625
|
+
content: exports_external.string(),
|
|
38626
|
+
timestamp: exports_external.number()
|
|
38627
|
+
});
|
|
38628
|
+
DiscussionInsightSchema = exports_external.object({
|
|
38629
|
+
id: exports_external.string(),
|
|
38630
|
+
type: exports_external.enum(["decision", "requirement", "idea", "concern", "learning"]),
|
|
38631
|
+
title: exports_external.string(),
|
|
38632
|
+
content: exports_external.string(),
|
|
38633
|
+
tags: exports_external.array(exports_external.string()).default([]),
|
|
38634
|
+
createdAt: exports_external.string()
|
|
38635
|
+
});
|
|
38636
|
+
DiscussionSchema = exports_external.object({
|
|
38637
|
+
id: exports_external.string(),
|
|
38638
|
+
title: exports_external.string(),
|
|
38639
|
+
topic: exports_external.string(),
|
|
38640
|
+
status: exports_external.enum(["active", "completed", "archived"]).default("active"),
|
|
38641
|
+
messages: exports_external.array(DiscussionMessageSchema).default([]),
|
|
38642
|
+
insights: exports_external.array(DiscussionInsightSchema).default([]),
|
|
38643
|
+
createdAt: exports_external.string(),
|
|
38644
|
+
updatedAt: exports_external.string(),
|
|
38645
|
+
metadata: exports_external.object({
|
|
38646
|
+
model: exports_external.string(),
|
|
38647
|
+
provider: exports_external.string()
|
|
38648
|
+
})
|
|
38649
|
+
});
|
|
38650
|
+
});
|
|
38651
|
+
|
|
38614
38652
|
// ../sdk/src/index.ts
|
|
38615
38653
|
class LocusClient {
|
|
38616
38654
|
api;
|
|
@@ -38712,6 +38750,7 @@ var init_src2 = __esm(() => {
|
|
|
38712
38750
|
init_sprints();
|
|
38713
38751
|
init_tasks();
|
|
38714
38752
|
init_workspaces();
|
|
38753
|
+
init_discussion_types();
|
|
38715
38754
|
init_events();
|
|
38716
38755
|
init_auth();
|
|
38717
38756
|
init_ci();
|
|
@@ -38936,10 +38975,198 @@ var init_reviewer_worker = __esm(() => {
|
|
|
38936
38975
|
}
|
|
38937
38976
|
});
|
|
38938
38977
|
|
|
38939
|
-
// ../sdk/src/
|
|
38940
|
-
import {
|
|
38978
|
+
// ../sdk/src/discussion/discussion-manager.ts
|
|
38979
|
+
import {
|
|
38980
|
+
existsSync as existsSync5,
|
|
38981
|
+
mkdirSync as mkdirSync3,
|
|
38982
|
+
readdirSync as readdirSync2,
|
|
38983
|
+
readFileSync as readFileSync5,
|
|
38984
|
+
unlinkSync as unlinkSync2,
|
|
38985
|
+
writeFileSync as writeFileSync3
|
|
38986
|
+
} from "node:fs";
|
|
38941
38987
|
import { join as join6 } from "node:path";
|
|
38942
38988
|
|
|
38989
|
+
class DiscussionManager {
|
|
38990
|
+
discussionsDir;
|
|
38991
|
+
constructor(projectPath) {
|
|
38992
|
+
this.discussionsDir = getLocusPath(projectPath, "discussionsDir");
|
|
38993
|
+
}
|
|
38994
|
+
create(topic, model, provider) {
|
|
38995
|
+
this.ensureDir();
|
|
38996
|
+
const now = new Date().toISOString();
|
|
38997
|
+
const id = `disc-${Date.now()}`;
|
|
38998
|
+
const discussion = {
|
|
38999
|
+
id,
|
|
39000
|
+
title: topic,
|
|
39001
|
+
topic,
|
|
39002
|
+
status: "active",
|
|
39003
|
+
messages: [],
|
|
39004
|
+
insights: [],
|
|
39005
|
+
createdAt: now,
|
|
39006
|
+
updatedAt: now,
|
|
39007
|
+
metadata: { model, provider }
|
|
39008
|
+
};
|
|
39009
|
+
this.save(discussion);
|
|
39010
|
+
return discussion;
|
|
39011
|
+
}
|
|
39012
|
+
save(discussion) {
|
|
39013
|
+
this.ensureDir();
|
|
39014
|
+
const jsonPath = join6(this.discussionsDir, `${discussion.id}.json`);
|
|
39015
|
+
const mdPath = join6(this.discussionsDir, `summary-${discussion.id}.md`);
|
|
39016
|
+
writeFileSync3(jsonPath, JSON.stringify(discussion, null, 2), "utf-8");
|
|
39017
|
+
writeFileSync3(mdPath, this.toMarkdown(discussion), "utf-8");
|
|
39018
|
+
}
|
|
39019
|
+
load(id) {
|
|
39020
|
+
this.ensureDir();
|
|
39021
|
+
const filePath = join6(this.discussionsDir, `${id}.json`);
|
|
39022
|
+
if (!existsSync5(filePath)) {
|
|
39023
|
+
return null;
|
|
39024
|
+
}
|
|
39025
|
+
try {
|
|
39026
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
39027
|
+
} catch {
|
|
39028
|
+
return null;
|
|
39029
|
+
}
|
|
39030
|
+
}
|
|
39031
|
+
list(status) {
|
|
39032
|
+
this.ensureDir();
|
|
39033
|
+
const files = readdirSync2(this.discussionsDir).filter((f) => f.endsWith(".json"));
|
|
39034
|
+
const discussions = [];
|
|
39035
|
+
for (const file2 of files) {
|
|
39036
|
+
try {
|
|
39037
|
+
const discussion = JSON.parse(readFileSync5(join6(this.discussionsDir, file2), "utf-8"));
|
|
39038
|
+
if (!status || discussion.status === status) {
|
|
39039
|
+
discussions.push(discussion);
|
|
39040
|
+
}
|
|
39041
|
+
} catch {}
|
|
39042
|
+
}
|
|
39043
|
+
discussions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
39044
|
+
return discussions;
|
|
39045
|
+
}
|
|
39046
|
+
complete(id) {
|
|
39047
|
+
const discussion = this.load(id);
|
|
39048
|
+
if (!discussion) {
|
|
39049
|
+
throw new Error(`Discussion not found: ${id}`);
|
|
39050
|
+
}
|
|
39051
|
+
discussion.status = "completed";
|
|
39052
|
+
discussion.updatedAt = new Date().toISOString();
|
|
39053
|
+
this.save(discussion);
|
|
39054
|
+
return discussion;
|
|
39055
|
+
}
|
|
39056
|
+
archive(id) {
|
|
39057
|
+
const discussion = this.load(id);
|
|
39058
|
+
if (!discussion) {
|
|
39059
|
+
throw new Error(`Discussion not found: ${id}`);
|
|
39060
|
+
}
|
|
39061
|
+
discussion.status = "archived";
|
|
39062
|
+
discussion.updatedAt = new Date().toISOString();
|
|
39063
|
+
this.save(discussion);
|
|
39064
|
+
}
|
|
39065
|
+
delete(id) {
|
|
39066
|
+
this.ensureDir();
|
|
39067
|
+
const jsonPath = join6(this.discussionsDir, `${id}.json`);
|
|
39068
|
+
const mdPath = join6(this.discussionsDir, `summary-${id}.md`);
|
|
39069
|
+
if (existsSync5(jsonPath)) {
|
|
39070
|
+
unlinkSync2(jsonPath);
|
|
39071
|
+
}
|
|
39072
|
+
if (existsSync5(mdPath)) {
|
|
39073
|
+
unlinkSync2(mdPath);
|
|
39074
|
+
}
|
|
39075
|
+
}
|
|
39076
|
+
addMessage(id, role, content) {
|
|
39077
|
+
const discussion = this.load(id);
|
|
39078
|
+
if (!discussion) {
|
|
39079
|
+
throw new Error(`Discussion not found: ${id}`);
|
|
39080
|
+
}
|
|
39081
|
+
discussion.messages.push({
|
|
39082
|
+
role,
|
|
39083
|
+
content,
|
|
39084
|
+
timestamp: Date.now()
|
|
39085
|
+
});
|
|
39086
|
+
discussion.updatedAt = new Date().toISOString();
|
|
39087
|
+
this.save(discussion);
|
|
39088
|
+
return discussion;
|
|
39089
|
+
}
|
|
39090
|
+
addInsight(id, insight) {
|
|
39091
|
+
const discussion = this.load(id);
|
|
39092
|
+
if (!discussion) {
|
|
39093
|
+
throw new Error(`Discussion not found: ${id}`);
|
|
39094
|
+
}
|
|
39095
|
+
discussion.insights.push(insight);
|
|
39096
|
+
discussion.updatedAt = new Date().toISOString();
|
|
39097
|
+
this.save(discussion);
|
|
39098
|
+
return discussion;
|
|
39099
|
+
}
|
|
39100
|
+
getAllInsights() {
|
|
39101
|
+
const discussions = this.list("completed");
|
|
39102
|
+
const insights = [];
|
|
39103
|
+
for (const discussion of discussions) {
|
|
39104
|
+
insights.push(...discussion.insights);
|
|
39105
|
+
}
|
|
39106
|
+
insights.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
39107
|
+
return insights;
|
|
39108
|
+
}
|
|
39109
|
+
getMarkdown(id) {
|
|
39110
|
+
const discussion = this.load(id);
|
|
39111
|
+
if (!discussion)
|
|
39112
|
+
return null;
|
|
39113
|
+
return this.toMarkdown(discussion);
|
|
39114
|
+
}
|
|
39115
|
+
toMarkdown(discussion) {
|
|
39116
|
+
const lines = [];
|
|
39117
|
+
lines.push(`# Discussion: ${discussion.title}`);
|
|
39118
|
+
lines.push("");
|
|
39119
|
+
lines.push(`**Status:** ${discussion.status.toUpperCase()}`);
|
|
39120
|
+
lines.push(`**Topic:** ${discussion.topic}`);
|
|
39121
|
+
lines.push(`**Created:** ${discussion.createdAt}`);
|
|
39122
|
+
lines.push(`**Updated:** ${discussion.updatedAt}`);
|
|
39123
|
+
lines.push(`**Model:** ${discussion.metadata.model} (${discussion.metadata.provider})`);
|
|
39124
|
+
lines.push("");
|
|
39125
|
+
if (discussion.messages.length > 0) {
|
|
39126
|
+
lines.push(`## Messages (${discussion.messages.length})`);
|
|
39127
|
+
lines.push("");
|
|
39128
|
+
for (const msg of discussion.messages) {
|
|
39129
|
+
const time3 = new Date(msg.timestamp).toISOString();
|
|
39130
|
+
const roleLabel = msg.role === "user" ? "User" : "Assistant";
|
|
39131
|
+
lines.push(`### ${roleLabel} — ${time3}`);
|
|
39132
|
+
lines.push("");
|
|
39133
|
+
lines.push(msg.content);
|
|
39134
|
+
lines.push("");
|
|
39135
|
+
}
|
|
39136
|
+
}
|
|
39137
|
+
if (discussion.insights.length > 0) {
|
|
39138
|
+
lines.push(`## Insights (${discussion.insights.length})`);
|
|
39139
|
+
lines.push("");
|
|
39140
|
+
for (const insight of discussion.insights) {
|
|
39141
|
+
lines.push(`### [${insight.type.toUpperCase()}] ${insight.title}`);
|
|
39142
|
+
lines.push("");
|
|
39143
|
+
lines.push(insight.content);
|
|
39144
|
+
if (insight.tags.length > 0) {
|
|
39145
|
+
lines.push("");
|
|
39146
|
+
lines.push(`**Tags:** ${insight.tags.join(", ")}`);
|
|
39147
|
+
}
|
|
39148
|
+
lines.push("");
|
|
39149
|
+
}
|
|
39150
|
+
}
|
|
39151
|
+
lines.push("---");
|
|
39152
|
+
lines.push(`*Discussion ID: ${discussion.id}*`);
|
|
39153
|
+
return lines.join(`
|
|
39154
|
+
`);
|
|
39155
|
+
}
|
|
39156
|
+
ensureDir() {
|
|
39157
|
+
if (!existsSync5(this.discussionsDir)) {
|
|
39158
|
+
mkdirSync3(this.discussionsDir, { recursive: true });
|
|
39159
|
+
}
|
|
39160
|
+
}
|
|
39161
|
+
}
|
|
39162
|
+
var init_discussion_manager = __esm(() => {
|
|
39163
|
+
init_config();
|
|
39164
|
+
});
|
|
39165
|
+
|
|
39166
|
+
// ../sdk/src/core/prompt-builder.ts
|
|
39167
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
39168
|
+
import { join as join7 } from "node:path";
|
|
39169
|
+
|
|
38943
39170
|
class PromptBuilder {
|
|
38944
39171
|
projectPath;
|
|
38945
39172
|
constructor(projectPath) {
|
|
@@ -38977,6 +39204,15 @@ ${knowledgeBase}
|
|
|
38977
39204
|
These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
|
|
38978
39205
|
${learnings}
|
|
38979
39206
|
</learnings>
|
|
39207
|
+
`;
|
|
39208
|
+
}
|
|
39209
|
+
const discussionInsights = this.getDiscussionInsightsContent();
|
|
39210
|
+
if (discussionInsights) {
|
|
39211
|
+
sections += `
|
|
39212
|
+
<discussion_insights>
|
|
39213
|
+
These are key decisions and insights from product discussions. Follow them to maintain product coherence:
|
|
39214
|
+
${discussionInsights}
|
|
39215
|
+
</discussion_insights>
|
|
38980
39216
|
`;
|
|
38981
39217
|
}
|
|
38982
39218
|
if (task2.docs && task2.docs.length > 0) {
|
|
@@ -39065,6 +39301,15 @@ ${knowledgeBase}
|
|
|
39065
39301
|
These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
|
|
39066
39302
|
${learnings}
|
|
39067
39303
|
</learnings>
|
|
39304
|
+
`;
|
|
39305
|
+
}
|
|
39306
|
+
const discussionInsights = this.getDiscussionInsightsContent();
|
|
39307
|
+
if (discussionInsights) {
|
|
39308
|
+
sections += `
|
|
39309
|
+
<discussion_insights>
|
|
39310
|
+
These are key decisions and insights from product discussions. Follow them to maintain product coherence:
|
|
39311
|
+
${discussionInsights}
|
|
39312
|
+
</discussion_insights>
|
|
39068
39313
|
`;
|
|
39069
39314
|
}
|
|
39070
39315
|
return `<direct_execution>
|
|
@@ -39079,9 +39324,9 @@ ${sections}
|
|
|
39079
39324
|
}
|
|
39080
39325
|
getProjectContext() {
|
|
39081
39326
|
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
39082
|
-
if (
|
|
39327
|
+
if (existsSync6(contextPath)) {
|
|
39083
39328
|
try {
|
|
39084
|
-
const context2 =
|
|
39329
|
+
const context2 = readFileSync6(contextPath, "utf-8");
|
|
39085
39330
|
if (context2.trim().length > 20) {
|
|
39086
39331
|
return context2;
|
|
39087
39332
|
}
|
|
@@ -39092,10 +39337,10 @@ ${sections}
|
|
|
39092
39337
|
return this.getFallbackContext() || null;
|
|
39093
39338
|
}
|
|
39094
39339
|
getFallbackContext() {
|
|
39095
|
-
const readmePath =
|
|
39096
|
-
if (
|
|
39340
|
+
const readmePath = join7(this.projectPath, "README.md");
|
|
39341
|
+
if (existsSync6(readmePath)) {
|
|
39097
39342
|
try {
|
|
39098
|
-
const content =
|
|
39343
|
+
const content = readFileSync6(readmePath, "utf-8");
|
|
39099
39344
|
const limit = 1000;
|
|
39100
39345
|
return content.slice(0, limit) + (content.length > limit ? `
|
|
39101
39346
|
...(truncated)...` : "");
|
|
@@ -39109,15 +39354,16 @@ ${sections}
|
|
|
39109
39354
|
return `You have access to the following documentation directories for context:
|
|
39110
39355
|
- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
|
|
39111
39356
|
- Documents: \`.locus/documents\` (synced from cloud)
|
|
39357
|
+
- Discussions: \`.locus/discussions\` (product discussion insights and decisions)
|
|
39112
39358
|
If you need more information about the project strategies, plans, or architecture, read files in these directories.`;
|
|
39113
39359
|
}
|
|
39114
39360
|
getLearningsContent() {
|
|
39115
39361
|
const learningsPath = getLocusPath(this.projectPath, "learningsFile");
|
|
39116
|
-
if (!
|
|
39362
|
+
if (!existsSync6(learningsPath)) {
|
|
39117
39363
|
return null;
|
|
39118
39364
|
}
|
|
39119
39365
|
try {
|
|
39120
|
-
const content =
|
|
39366
|
+
const content = readFileSync6(learningsPath, "utf-8");
|
|
39121
39367
|
const lines = content.split(`
|
|
39122
39368
|
`).filter((l) => l.startsWith("- "));
|
|
39123
39369
|
if (lines.length === 0) {
|
|
@@ -39129,6 +39375,53 @@ If you need more information about the project strategies, plans, or architectur
|
|
|
39129
39375
|
return null;
|
|
39130
39376
|
}
|
|
39131
39377
|
}
|
|
39378
|
+
getDiscussionInsightsContent() {
|
|
39379
|
+
try {
|
|
39380
|
+
const manager = new DiscussionManager(this.projectPath);
|
|
39381
|
+
const insights = manager.getAllInsights();
|
|
39382
|
+
if (insights.length === 0) {
|
|
39383
|
+
return null;
|
|
39384
|
+
}
|
|
39385
|
+
const groups = {};
|
|
39386
|
+
for (const insight of insights) {
|
|
39387
|
+
const key = insight.type;
|
|
39388
|
+
if (!groups[key]) {
|
|
39389
|
+
groups[key] = [];
|
|
39390
|
+
}
|
|
39391
|
+
groups[key].push(insight);
|
|
39392
|
+
}
|
|
39393
|
+
const typeLabels = {
|
|
39394
|
+
decision: "Decisions",
|
|
39395
|
+
requirement: "Requirements",
|
|
39396
|
+
idea: "Ideas",
|
|
39397
|
+
concern: "Concerns",
|
|
39398
|
+
learning: "Learnings"
|
|
39399
|
+
};
|
|
39400
|
+
let output = "";
|
|
39401
|
+
for (const [type, label] of Object.entries(typeLabels)) {
|
|
39402
|
+
const items = groups[type];
|
|
39403
|
+
if (!items || items.length === 0)
|
|
39404
|
+
continue;
|
|
39405
|
+
output += `## ${label}
|
|
39406
|
+
`;
|
|
39407
|
+
for (const item of items) {
|
|
39408
|
+
output += `- [${item.title}]: ${item.content}
|
|
39409
|
+
`;
|
|
39410
|
+
}
|
|
39411
|
+
output += `
|
|
39412
|
+
`;
|
|
39413
|
+
}
|
|
39414
|
+
if (output.length === 0) {
|
|
39415
|
+
return null;
|
|
39416
|
+
}
|
|
39417
|
+
if (output.length > 2000) {
|
|
39418
|
+
output = `${output.slice(0, 1997)}...`;
|
|
39419
|
+
}
|
|
39420
|
+
return output.trimEnd();
|
|
39421
|
+
} catch {
|
|
39422
|
+
return null;
|
|
39423
|
+
}
|
|
39424
|
+
}
|
|
39132
39425
|
roleToText(role) {
|
|
39133
39426
|
if (!role) {
|
|
39134
39427
|
return null;
|
|
@@ -39151,6 +39444,7 @@ If you need more information about the project strategies, plans, or architectur
|
|
|
39151
39444
|
}
|
|
39152
39445
|
var init_prompt_builder = __esm(() => {
|
|
39153
39446
|
init_src();
|
|
39447
|
+
init_discussion_manager();
|
|
39154
39448
|
init_config();
|
|
39155
39449
|
});
|
|
39156
39450
|
|
|
@@ -39467,7 +39761,6 @@ Branch: \`${result.branch}\`` : "";
|
|
|
39467
39761
|
this.log("All tasks done. Creating pull request...", "info");
|
|
39468
39762
|
const prResult = this.gitWorkflow.createPullRequest(this.completedTaskList, this.taskSummaries);
|
|
39469
39763
|
if (prResult.url) {
|
|
39470
|
-
this.log(`PR created: ${prResult.url}`, "success");
|
|
39471
39764
|
for (const task2 of this.completedTaskList) {
|
|
39472
39765
|
try {
|
|
39473
39766
|
await this.client.tasks.update(task2.id, this.config.workspaceId, {
|
|
@@ -39535,6 +39828,349 @@ var init_core3 = __esm(() => {
|
|
|
39535
39828
|
init_prompt_builder();
|
|
39536
39829
|
});
|
|
39537
39830
|
|
|
39831
|
+
// ../sdk/src/discussion/agents/facilitator-prompt.ts
|
|
39832
|
+
function buildFacilitatorPrompt(input) {
|
|
39833
|
+
const {
|
|
39834
|
+
topic,
|
|
39835
|
+
projectContext,
|
|
39836
|
+
learnings,
|
|
39837
|
+
knowledgeBase,
|
|
39838
|
+
previousMessages,
|
|
39839
|
+
insights,
|
|
39840
|
+
isFirstMessage
|
|
39841
|
+
} = input;
|
|
39842
|
+
let sections = "";
|
|
39843
|
+
if (projectContext) {
|
|
39844
|
+
sections += `
|
|
39845
|
+
<project_context>
|
|
39846
|
+
${projectContext}
|
|
39847
|
+
</project_context>
|
|
39848
|
+
`;
|
|
39849
|
+
}
|
|
39850
|
+
sections += `
|
|
39851
|
+
<knowledge_base>
|
|
39852
|
+
${knowledgeBase}
|
|
39853
|
+
</knowledge_base>
|
|
39854
|
+
`;
|
|
39855
|
+
if (learnings) {
|
|
39856
|
+
sections += `
|
|
39857
|
+
<learnings>
|
|
39858
|
+
These are accumulated lessons from past work on this project. Use them to ask more informed questions:
|
|
39859
|
+
${learnings}
|
|
39860
|
+
</learnings>
|
|
39861
|
+
`;
|
|
39862
|
+
}
|
|
39863
|
+
if (previousMessages.length > 0) {
|
|
39864
|
+
let history = "";
|
|
39865
|
+
for (const msg of previousMessages) {
|
|
39866
|
+
const role = msg.role === "user" ? "User" : "Facilitator";
|
|
39867
|
+
history += `[${role}]: ${msg.content}
|
|
39868
|
+
|
|
39869
|
+
`;
|
|
39870
|
+
}
|
|
39871
|
+
sections += `
|
|
39872
|
+
<conversation_history>
|
|
39873
|
+
${history.trimEnd()}
|
|
39874
|
+
</conversation_history>
|
|
39875
|
+
`;
|
|
39876
|
+
}
|
|
39877
|
+
if (insights.length > 0) {
|
|
39878
|
+
let insightsText = "";
|
|
39879
|
+
for (const insight of insights) {
|
|
39880
|
+
insightsText += `- [${insight.type.toUpperCase()}] ${insight.title}: ${insight.content}
|
|
39881
|
+
`;
|
|
39882
|
+
}
|
|
39883
|
+
sections += `
|
|
39884
|
+
<extracted_insights>
|
|
39885
|
+
Insights identified so far in this discussion:
|
|
39886
|
+
${insightsText.trimEnd()}
|
|
39887
|
+
</extracted_insights>
|
|
39888
|
+
`;
|
|
39889
|
+
}
|
|
39890
|
+
const firstMessageInstruction = isFirstMessage ? `This is the START of the discussion. Introduce yourself briefly, then ask your first probing question about the topic. Do NOT extract any insights yet — there is no user input to extract from.` : `Continue the discussion by responding to the user's latest message. Build on their answers to go deeper. After responding, extract any insights from their message.`;
|
|
39891
|
+
return `<discussion_facilitator>
|
|
39892
|
+
You are a product strategy facilitator leading a structured discussion.
|
|
39893
|
+
|
|
39894
|
+
<topic>
|
|
39895
|
+
${topic}
|
|
39896
|
+
</topic>
|
|
39897
|
+
${sections}
|
|
39898
|
+
<role>
|
|
39899
|
+
You are an expert product strategy facilitator. Your job is to:
|
|
39900
|
+
1. Ask probing, specific questions about the topic — never generic or surface-level
|
|
39901
|
+
2. Build on previous answers to progressively deepen the conversation
|
|
39902
|
+
3. Identify and extract key decisions, requirements, ideas, concerns, and learnings
|
|
39903
|
+
4. Reference existing project context and learnings to ask informed questions
|
|
39904
|
+
5. When the topic feels fully explored, suggest wrapping up with a summary
|
|
39905
|
+
</role>
|
|
39906
|
+
|
|
39907
|
+
<rules>
|
|
39908
|
+
- ${firstMessageInstruction}
|
|
39909
|
+
- Ask ONE focused question at a time. Do not overwhelm with multiple questions.
|
|
39910
|
+
- Be conversational but purposeful — every question should drive toward actionable insights.
|
|
39911
|
+
- When you identify an insight from the user's response, include it as a structured XML block in your response.
|
|
39912
|
+
- Insight blocks use this format within your response text:
|
|
39913
|
+
|
|
39914
|
+
<insight>
|
|
39915
|
+
{"type": "decision|requirement|idea|concern|learning", "title": "short title", "content": "detailed description", "tags": ["relevant", "tags"]}
|
|
39916
|
+
</insight>
|
|
39917
|
+
|
|
39918
|
+
- You may include multiple <insight> blocks if the user's response contains several distinct insights.
|
|
39919
|
+
- The insight blocks will be parsed and removed from the displayed response, so write your conversational text as if they are not there.
|
|
39920
|
+
- Types explained:
|
|
39921
|
+
- **decision**: A choice or direction that has been made or agreed upon
|
|
39922
|
+
- **requirement**: A specific need, constraint, or must-have
|
|
39923
|
+
- **idea**: A suggestion, proposal, or possibility to explore
|
|
39924
|
+
- **concern**: A risk, worry, or potential problem identified
|
|
39925
|
+
- **learning**: A realization, lesson, or important context discovered
|
|
39926
|
+
- Keep responses concise. Aim for 2-4 sentences of conversation plus any insight blocks.
|
|
39927
|
+
- If the user's responses indicate the topic is well-explored, suggest summarizing and wrapping up.
|
|
39928
|
+
</rules>
|
|
39929
|
+
</discussion_facilitator>`;
|
|
39930
|
+
}
|
|
39931
|
+
function buildSummaryPrompt(topic, messages, insights) {
|
|
39932
|
+
let history = "";
|
|
39933
|
+
for (const msg of messages) {
|
|
39934
|
+
const role = msg.role === "user" ? "User" : "Facilitator";
|
|
39935
|
+
history += `[${role}]: ${msg.content}
|
|
39936
|
+
|
|
39937
|
+
`;
|
|
39938
|
+
}
|
|
39939
|
+
let insightsText = "";
|
|
39940
|
+
if (insights.length > 0) {
|
|
39941
|
+
for (const insight of insights) {
|
|
39942
|
+
insightsText += `- [${insight.type.toUpperCase()}] **${insight.title}**: ${insight.content}`;
|
|
39943
|
+
if (insight.tags.length > 0) {
|
|
39944
|
+
insightsText += ` (tags: ${insight.tags.join(", ")})`;
|
|
39945
|
+
}
|
|
39946
|
+
insightsText += `
|
|
39947
|
+
`;
|
|
39948
|
+
}
|
|
39949
|
+
}
|
|
39950
|
+
return `<discussion_summary>
|
|
39951
|
+
Create a final summary of this product discussion.
|
|
39952
|
+
|
|
39953
|
+
<topic>
|
|
39954
|
+
${topic}
|
|
39955
|
+
</topic>
|
|
39956
|
+
|
|
39957
|
+
<conversation>
|
|
39958
|
+
${history.trimEnd()}
|
|
39959
|
+
</conversation>
|
|
39960
|
+
|
|
39961
|
+
${insightsText ? `<insights>
|
|
39962
|
+
${insightsText.trimEnd()}
|
|
39963
|
+
</insights>
|
|
39964
|
+
` : ""}
|
|
39965
|
+
<rules>
|
|
39966
|
+
- Write a clear, structured summary of the entire discussion.
|
|
39967
|
+
- Organize by: Key Decisions, Requirements, Ideas to Explore, Concerns & Risks, and Learnings.
|
|
39968
|
+
- Only include sections that have relevant content — skip empty categories.
|
|
39969
|
+
- For each item, provide a brief but actionable description.
|
|
39970
|
+
- End with a "Next Steps" section listing concrete action items that emerged.
|
|
39971
|
+
- Be concise — this summary should be scannable and useful as a reference.
|
|
39972
|
+
- Do NOT include any <insight> XML blocks in the summary.
|
|
39973
|
+
</rules>
|
|
39974
|
+
</discussion_summary>`;
|
|
39975
|
+
}
|
|
39976
|
+
|
|
39977
|
+
// ../sdk/src/discussion/discussion-facilitator.ts
|
|
39978
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
39979
|
+
|
|
39980
|
+
class DiscussionFacilitator {
|
|
39981
|
+
projectPath;
|
|
39982
|
+
aiRunner;
|
|
39983
|
+
discussionManager;
|
|
39984
|
+
log;
|
|
39985
|
+
provider;
|
|
39986
|
+
model;
|
|
39987
|
+
constructor(config2) {
|
|
39988
|
+
this.projectPath = config2.projectPath;
|
|
39989
|
+
this.aiRunner = config2.aiRunner;
|
|
39990
|
+
this.discussionManager = config2.discussionManager;
|
|
39991
|
+
this.log = config2.log ?? ((_msg) => {
|
|
39992
|
+
return;
|
|
39993
|
+
});
|
|
39994
|
+
this.provider = config2.provider;
|
|
39995
|
+
this.model = config2.model;
|
|
39996
|
+
}
|
|
39997
|
+
async startDiscussion(topic) {
|
|
39998
|
+
this.log("Starting new discussion...", "info");
|
|
39999
|
+
const discussion = this.discussionManager.create(topic, this.model, this.provider);
|
|
40000
|
+
const { projectContext, learnings, knowledgeBase } = this.buildContext();
|
|
40001
|
+
const prompt = buildFacilitatorPrompt({
|
|
40002
|
+
topic,
|
|
40003
|
+
projectContext,
|
|
40004
|
+
learnings,
|
|
40005
|
+
knowledgeBase,
|
|
40006
|
+
previousMessages: [],
|
|
40007
|
+
insights: [],
|
|
40008
|
+
isFirstMessage: true
|
|
40009
|
+
});
|
|
40010
|
+
const response = await this.aiRunner.run(prompt);
|
|
40011
|
+
const { cleanResponse } = this.parseInsights(response);
|
|
40012
|
+
this.discussionManager.addMessage(discussion.id, "assistant", cleanResponse);
|
|
40013
|
+
this.log("Discussion started", "success");
|
|
40014
|
+
const saved = this.discussionManager.load(discussion.id);
|
|
40015
|
+
if (!saved) {
|
|
40016
|
+
throw new Error(`Failed to load discussion after creation: ${discussion.id}`);
|
|
40017
|
+
}
|
|
40018
|
+
return {
|
|
40019
|
+
discussion: saved,
|
|
40020
|
+
message: cleanResponse
|
|
40021
|
+
};
|
|
40022
|
+
}
|
|
40023
|
+
async continueDiscussion(discussionId, userMessage) {
|
|
40024
|
+
const discussion = this.discussionManager.load(discussionId);
|
|
40025
|
+
if (!discussion) {
|
|
40026
|
+
throw new Error(`Discussion not found: ${discussionId}`);
|
|
40027
|
+
}
|
|
40028
|
+
const updated = this.discussionManager.addMessage(discussionId, "user", userMessage);
|
|
40029
|
+
const { projectContext, learnings, knowledgeBase } = this.buildContext();
|
|
40030
|
+
const prompt = buildFacilitatorPrompt({
|
|
40031
|
+
topic: updated.topic,
|
|
40032
|
+
projectContext,
|
|
40033
|
+
learnings,
|
|
40034
|
+
knowledgeBase,
|
|
40035
|
+
previousMessages: updated.messages,
|
|
40036
|
+
insights: updated.insights,
|
|
40037
|
+
isFirstMessage: false
|
|
40038
|
+
});
|
|
40039
|
+
const response = await this.aiRunner.run(prompt);
|
|
40040
|
+
const { cleanResponse, insights } = this.parseInsights(response);
|
|
40041
|
+
for (const insight of insights) {
|
|
40042
|
+
this.discussionManager.addInsight(discussionId, insight);
|
|
40043
|
+
}
|
|
40044
|
+
this.discussionManager.addMessage(discussionId, "assistant", cleanResponse);
|
|
40045
|
+
return { response: cleanResponse, insights };
|
|
40046
|
+
}
|
|
40047
|
+
async* continueDiscussionStream(discussionId, userMessage) {
|
|
40048
|
+
const discussion = this.discussionManager.load(discussionId);
|
|
40049
|
+
if (!discussion) {
|
|
40050
|
+
throw new Error(`Discussion not found: ${discussionId}`);
|
|
40051
|
+
}
|
|
40052
|
+
const updated = this.discussionManager.addMessage(discussionId, "user", userMessage);
|
|
40053
|
+
const { projectContext, learnings, knowledgeBase } = this.buildContext();
|
|
40054
|
+
const prompt = buildFacilitatorPrompt({
|
|
40055
|
+
topic: updated.topic,
|
|
40056
|
+
projectContext,
|
|
40057
|
+
learnings,
|
|
40058
|
+
knowledgeBase,
|
|
40059
|
+
previousMessages: updated.messages,
|
|
40060
|
+
insights: updated.insights,
|
|
40061
|
+
isFirstMessage: false
|
|
40062
|
+
});
|
|
40063
|
+
let fullResponse = "";
|
|
40064
|
+
const stream4 = this.aiRunner.runStream(prompt);
|
|
40065
|
+
for await (const chunk of stream4) {
|
|
40066
|
+
yield chunk;
|
|
40067
|
+
if (chunk.type === "text_delta") {
|
|
40068
|
+
fullResponse += chunk.content;
|
|
40069
|
+
} else if (chunk.type === "result") {
|
|
40070
|
+
fullResponse = chunk.content;
|
|
40071
|
+
}
|
|
40072
|
+
}
|
|
40073
|
+
const { cleanResponse, insights } = this.parseInsights(fullResponse);
|
|
40074
|
+
for (const insight of insights) {
|
|
40075
|
+
this.discussionManager.addInsight(discussionId, insight);
|
|
40076
|
+
}
|
|
40077
|
+
this.discussionManager.addMessage(discussionId, "assistant", cleanResponse);
|
|
40078
|
+
return { response: cleanResponse, insights };
|
|
40079
|
+
}
|
|
40080
|
+
async summarizeDiscussion(discussionId) {
|
|
40081
|
+
const discussion = this.discussionManager.load(discussionId);
|
|
40082
|
+
if (!discussion) {
|
|
40083
|
+
throw new Error(`Discussion not found: ${discussionId}`);
|
|
40084
|
+
}
|
|
40085
|
+
this.log("Generating discussion summary...", "info");
|
|
40086
|
+
const prompt = buildSummaryPrompt(discussion.topic, discussion.messages, discussion.insights);
|
|
40087
|
+
const summary = await this.aiRunner.run(prompt);
|
|
40088
|
+
this.discussionManager.addMessage(discussionId, "assistant", summary);
|
|
40089
|
+
this.discussionManager.complete(discussionId);
|
|
40090
|
+
this.log("Discussion summarized and completed", "success");
|
|
40091
|
+
return summary;
|
|
40092
|
+
}
|
|
40093
|
+
parseInsights(response) {
|
|
40094
|
+
const insights = [];
|
|
40095
|
+
const insightRegex = /<insight>\s*([\s\S]*?)\s*<\/insight>/g;
|
|
40096
|
+
let match = insightRegex.exec(response);
|
|
40097
|
+
while (match !== null) {
|
|
40098
|
+
try {
|
|
40099
|
+
const parsed = JSON.parse(match[1]);
|
|
40100
|
+
const id = `ins-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
|
|
40101
|
+
insights.push({
|
|
40102
|
+
id,
|
|
40103
|
+
type: parsed.type,
|
|
40104
|
+
title: parsed.title,
|
|
40105
|
+
content: parsed.content,
|
|
40106
|
+
tags: parsed.tags ?? [],
|
|
40107
|
+
createdAt: new Date().toISOString()
|
|
40108
|
+
});
|
|
40109
|
+
} catch {}
|
|
40110
|
+
match = insightRegex.exec(response);
|
|
40111
|
+
}
|
|
40112
|
+
const cleanResponse = response.replace(/<insight>\s*[\s\S]*?\s*<\/insight>/g, "").replace(/\n{3,}/g, `
|
|
40113
|
+
|
|
40114
|
+
`).trim();
|
|
40115
|
+
return { cleanResponse, insights };
|
|
40116
|
+
}
|
|
40117
|
+
buildContext() {
|
|
40118
|
+
return {
|
|
40119
|
+
projectContext: this.getProjectContext(),
|
|
40120
|
+
learnings: this.getLearningsContent(),
|
|
40121
|
+
knowledgeBase: this.getKnowledgeBaseSection()
|
|
40122
|
+
};
|
|
40123
|
+
}
|
|
40124
|
+
getProjectContext() {
|
|
40125
|
+
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
40126
|
+
if (existsSync7(contextPath)) {
|
|
40127
|
+
try {
|
|
40128
|
+
const context2 = readFileSync7(contextPath, "utf-8");
|
|
40129
|
+
if (context2.trim().length > 20) {
|
|
40130
|
+
return context2;
|
|
40131
|
+
}
|
|
40132
|
+
} catch {
|
|
40133
|
+
return null;
|
|
40134
|
+
}
|
|
40135
|
+
}
|
|
40136
|
+
return null;
|
|
40137
|
+
}
|
|
40138
|
+
getLearningsContent() {
|
|
40139
|
+
const learningsPath = getLocusPath(this.projectPath, "learningsFile");
|
|
40140
|
+
if (!existsSync7(learningsPath)) {
|
|
40141
|
+
return null;
|
|
40142
|
+
}
|
|
40143
|
+
try {
|
|
40144
|
+
const content = readFileSync7(learningsPath, "utf-8");
|
|
40145
|
+
const lines = content.split(`
|
|
40146
|
+
`).filter((l) => l.startsWith("- "));
|
|
40147
|
+
if (lines.length === 0) {
|
|
40148
|
+
return null;
|
|
40149
|
+
}
|
|
40150
|
+
return lines.join(`
|
|
40151
|
+
`);
|
|
40152
|
+
} catch {
|
|
40153
|
+
return null;
|
|
40154
|
+
}
|
|
40155
|
+
}
|
|
40156
|
+
getKnowledgeBaseSection() {
|
|
40157
|
+
return `You have access to the following documentation directories for context:
|
|
40158
|
+
- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
|
|
40159
|
+
- Documents: \`.locus/documents\` (synced from cloud)
|
|
40160
|
+
If you need more information about the project strategies, plans, or architecture, read files in these directories.`;
|
|
40161
|
+
}
|
|
40162
|
+
}
|
|
40163
|
+
var init_discussion_facilitator = __esm(() => {
|
|
40164
|
+
init_config();
|
|
40165
|
+
});
|
|
40166
|
+
|
|
40167
|
+
// ../sdk/src/discussion/index.ts
|
|
40168
|
+
var init_discussion = __esm(() => {
|
|
40169
|
+
init_discussion_facilitator();
|
|
40170
|
+
init_discussion_manager();
|
|
40171
|
+
init_discussion_types();
|
|
40172
|
+
});
|
|
40173
|
+
|
|
39538
40174
|
// ../sdk/src/exec/context-tracker.ts
|
|
39539
40175
|
function generateArtifactId() {
|
|
39540
40176
|
const timestamp = Date.now().toString(36);
|
|
@@ -39942,14 +40578,14 @@ var init_event_emitter = __esm(() => {
|
|
|
39942
40578
|
|
|
39943
40579
|
// ../sdk/src/exec/history-manager.ts
|
|
39944
40580
|
import {
|
|
39945
|
-
existsSync as
|
|
39946
|
-
mkdirSync as
|
|
39947
|
-
readdirSync as
|
|
39948
|
-
readFileSync as
|
|
40581
|
+
existsSync as existsSync8,
|
|
40582
|
+
mkdirSync as mkdirSync4,
|
|
40583
|
+
readdirSync as readdirSync3,
|
|
40584
|
+
readFileSync as readFileSync8,
|
|
39949
40585
|
rmSync,
|
|
39950
|
-
writeFileSync as
|
|
40586
|
+
writeFileSync as writeFileSync4
|
|
39951
40587
|
} from "node:fs";
|
|
39952
|
-
import { join as
|
|
40588
|
+
import { join as join8 } from "node:path";
|
|
39953
40589
|
function generateSessionId2() {
|
|
39954
40590
|
const timestamp = Date.now().toString(36);
|
|
39955
40591
|
const random = Math.random().toString(36).substring(2, 9);
|
|
@@ -39960,30 +40596,30 @@ class HistoryManager {
|
|
|
39960
40596
|
historyDir;
|
|
39961
40597
|
maxSessions;
|
|
39962
40598
|
constructor(projectPath, options) {
|
|
39963
|
-
this.historyDir = options?.historyDir ??
|
|
40599
|
+
this.historyDir = options?.historyDir ?? join8(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
|
|
39964
40600
|
this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
39965
40601
|
this.ensureHistoryDir();
|
|
39966
40602
|
}
|
|
39967
40603
|
ensureHistoryDir() {
|
|
39968
|
-
if (!
|
|
39969
|
-
|
|
40604
|
+
if (!existsSync8(this.historyDir)) {
|
|
40605
|
+
mkdirSync4(this.historyDir, { recursive: true });
|
|
39970
40606
|
}
|
|
39971
40607
|
}
|
|
39972
40608
|
getSessionPath(sessionId) {
|
|
39973
|
-
return
|
|
40609
|
+
return join8(this.historyDir, `${sessionId}.json`);
|
|
39974
40610
|
}
|
|
39975
40611
|
saveSession(session2) {
|
|
39976
40612
|
const filePath = this.getSessionPath(session2.id);
|
|
39977
40613
|
session2.updatedAt = Date.now();
|
|
39978
|
-
|
|
40614
|
+
writeFileSync4(filePath, JSON.stringify(session2, null, 2), "utf-8");
|
|
39979
40615
|
}
|
|
39980
40616
|
loadSession(sessionId) {
|
|
39981
40617
|
const filePath = this.getSessionPath(sessionId);
|
|
39982
|
-
if (!
|
|
40618
|
+
if (!existsSync8(filePath)) {
|
|
39983
40619
|
return null;
|
|
39984
40620
|
}
|
|
39985
40621
|
try {
|
|
39986
|
-
const content =
|
|
40622
|
+
const content = readFileSync8(filePath, "utf-8");
|
|
39987
40623
|
return JSON.parse(content);
|
|
39988
40624
|
} catch {
|
|
39989
40625
|
return null;
|
|
@@ -39991,7 +40627,7 @@ class HistoryManager {
|
|
|
39991
40627
|
}
|
|
39992
40628
|
deleteSession(sessionId) {
|
|
39993
40629
|
const filePath = this.getSessionPath(sessionId);
|
|
39994
|
-
if (!
|
|
40630
|
+
if (!existsSync8(filePath)) {
|
|
39995
40631
|
return false;
|
|
39996
40632
|
}
|
|
39997
40633
|
try {
|
|
@@ -40002,7 +40638,7 @@ class HistoryManager {
|
|
|
40002
40638
|
}
|
|
40003
40639
|
}
|
|
40004
40640
|
listSessions(options) {
|
|
40005
|
-
const files =
|
|
40641
|
+
const files = readdirSync3(this.historyDir);
|
|
40006
40642
|
let sessions = [];
|
|
40007
40643
|
for (const file2 of files) {
|
|
40008
40644
|
if (file2.endsWith(".json")) {
|
|
@@ -40075,11 +40711,11 @@ class HistoryManager {
|
|
|
40075
40711
|
return deleted;
|
|
40076
40712
|
}
|
|
40077
40713
|
getSessionCount() {
|
|
40078
|
-
const files =
|
|
40714
|
+
const files = readdirSync3(this.historyDir);
|
|
40079
40715
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
40080
40716
|
}
|
|
40081
40717
|
sessionExists(sessionId) {
|
|
40082
|
-
return
|
|
40718
|
+
return existsSync8(this.getSessionPath(sessionId));
|
|
40083
40719
|
}
|
|
40084
40720
|
findSessionByPartialId(partialId) {
|
|
40085
40721
|
const sessions = this.listSessions();
|
|
@@ -40093,12 +40729,12 @@ class HistoryManager {
|
|
|
40093
40729
|
return this.historyDir;
|
|
40094
40730
|
}
|
|
40095
40731
|
clearAllSessions() {
|
|
40096
|
-
const files =
|
|
40732
|
+
const files = readdirSync3(this.historyDir);
|
|
40097
40733
|
let deleted = 0;
|
|
40098
40734
|
for (const file2 of files) {
|
|
40099
40735
|
if (file2.endsWith(".json")) {
|
|
40100
40736
|
try {
|
|
40101
|
-
rmSync(
|
|
40737
|
+
rmSync(join8(this.historyDir, file2));
|
|
40102
40738
|
deleted++;
|
|
40103
40739
|
} catch {}
|
|
40104
40740
|
}
|
|
@@ -40387,8 +41023,8 @@ var init_git = __esm(() => {
|
|
|
40387
41023
|
|
|
40388
41024
|
// ../sdk/src/orchestrator/index.ts
|
|
40389
41025
|
import { spawn as spawn3 } from "node:child_process";
|
|
40390
|
-
import { existsSync as
|
|
40391
|
-
import { dirname as dirname2, join as
|
|
41026
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
41027
|
+
import { dirname as dirname2, join as join9 } from "node:path";
|
|
40392
41028
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40393
41029
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
40394
41030
|
function killProcessTree(proc) {
|
|
@@ -40677,12 +41313,12 @@ ${agentId} finished (exit code: ${code})`);
|
|
|
40677
41313
|
const currentModulePath = fileURLToPath2(import.meta.url);
|
|
40678
41314
|
const currentModuleDir = dirname2(currentModulePath);
|
|
40679
41315
|
const potentialPaths = [
|
|
40680
|
-
|
|
40681
|
-
|
|
40682
|
-
|
|
40683
|
-
|
|
41316
|
+
join9(currentModuleDir, "..", "agent", "worker.js"),
|
|
41317
|
+
join9(currentModuleDir, "agent", "worker.js"),
|
|
41318
|
+
join9(currentModuleDir, "worker.js"),
|
|
41319
|
+
join9(currentModuleDir, "..", "agent", "worker.ts")
|
|
40684
41320
|
];
|
|
40685
|
-
return potentialPaths.find((p) =>
|
|
41321
|
+
return potentialPaths.find((p) => existsSync9(p));
|
|
40686
41322
|
}
|
|
40687
41323
|
};
|
|
40688
41324
|
});
|
|
@@ -40794,14 +41430,14 @@ var init_sprint_plan = __esm(() => {
|
|
|
40794
41430
|
|
|
40795
41431
|
// ../sdk/src/planning/plan-manager.ts
|
|
40796
41432
|
import {
|
|
40797
|
-
existsSync as
|
|
40798
|
-
mkdirSync as
|
|
40799
|
-
readdirSync as
|
|
40800
|
-
readFileSync as
|
|
40801
|
-
unlinkSync as
|
|
40802
|
-
writeFileSync as
|
|
41433
|
+
existsSync as existsSync10,
|
|
41434
|
+
mkdirSync as mkdirSync5,
|
|
41435
|
+
readdirSync as readdirSync4,
|
|
41436
|
+
readFileSync as readFileSync9,
|
|
41437
|
+
unlinkSync as unlinkSync3,
|
|
41438
|
+
writeFileSync as writeFileSync5
|
|
40803
41439
|
} from "node:fs";
|
|
40804
|
-
import { join as
|
|
41440
|
+
import { join as join10 } from "node:path";
|
|
40805
41441
|
|
|
40806
41442
|
class PlanManager {
|
|
40807
41443
|
plansDir;
|
|
@@ -40811,19 +41447,19 @@ class PlanManager {
|
|
|
40811
41447
|
save(plan) {
|
|
40812
41448
|
this.ensurePlansDir();
|
|
40813
41449
|
const slug = this.slugify(plan.name);
|
|
40814
|
-
const jsonPath =
|
|
40815
|
-
const mdPath =
|
|
40816
|
-
|
|
40817
|
-
|
|
41450
|
+
const jsonPath = join10(this.plansDir, `${slug}.json`);
|
|
41451
|
+
const mdPath = join10(this.plansDir, `sprint-${slug}.md`);
|
|
41452
|
+
writeFileSync5(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
41453
|
+
writeFileSync5(mdPath, sprintPlanToMarkdown(plan), "utf-8");
|
|
40818
41454
|
return plan.id;
|
|
40819
41455
|
}
|
|
40820
41456
|
load(idOrSlug) {
|
|
40821
41457
|
this.ensurePlansDir();
|
|
40822
|
-
const files =
|
|
41458
|
+
const files = readdirSync4(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
40823
41459
|
for (const file2 of files) {
|
|
40824
|
-
const filePath =
|
|
41460
|
+
const filePath = join10(this.plansDir, file2);
|
|
40825
41461
|
try {
|
|
40826
|
-
const plan = JSON.parse(
|
|
41462
|
+
const plan = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
40827
41463
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
40828
41464
|
return plan;
|
|
40829
41465
|
}
|
|
@@ -40833,11 +41469,11 @@ class PlanManager {
|
|
|
40833
41469
|
}
|
|
40834
41470
|
list(status) {
|
|
40835
41471
|
this.ensurePlansDir();
|
|
40836
|
-
const files =
|
|
41472
|
+
const files = readdirSync4(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
40837
41473
|
const plans = [];
|
|
40838
41474
|
for (const file2 of files) {
|
|
40839
41475
|
try {
|
|
40840
|
-
const plan = JSON.parse(
|
|
41476
|
+
const plan = JSON.parse(readFileSync9(join10(this.plansDir, file2), "utf-8"));
|
|
40841
41477
|
if (!status || plan.status === status) {
|
|
40842
41478
|
plans.push(plan);
|
|
40843
41479
|
}
|
|
@@ -40894,18 +41530,18 @@ class PlanManager {
|
|
|
40894
41530
|
}
|
|
40895
41531
|
delete(idOrSlug) {
|
|
40896
41532
|
this.ensurePlansDir();
|
|
40897
|
-
const files =
|
|
41533
|
+
const files = readdirSync4(this.plansDir);
|
|
40898
41534
|
for (const file2 of files) {
|
|
40899
|
-
const filePath =
|
|
41535
|
+
const filePath = join10(this.plansDir, file2);
|
|
40900
41536
|
if (!file2.endsWith(".json"))
|
|
40901
41537
|
continue;
|
|
40902
41538
|
try {
|
|
40903
|
-
const plan = JSON.parse(
|
|
41539
|
+
const plan = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
40904
41540
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
40905
|
-
|
|
40906
|
-
const mdPath =
|
|
40907
|
-
if (
|
|
40908
|
-
|
|
41541
|
+
unlinkSync3(filePath);
|
|
41542
|
+
const mdPath = join10(this.plansDir, `sprint-${this.slugify(plan.name)}.md`);
|
|
41543
|
+
if (existsSync10(mdPath)) {
|
|
41544
|
+
unlinkSync3(mdPath);
|
|
40909
41545
|
}
|
|
40910
41546
|
return;
|
|
40911
41547
|
}
|
|
@@ -40919,8 +41555,8 @@ class PlanManager {
|
|
|
40919
41555
|
return sprintPlanToMarkdown(plan);
|
|
40920
41556
|
}
|
|
40921
41557
|
ensurePlansDir() {
|
|
40922
|
-
if (!
|
|
40923
|
-
|
|
41558
|
+
if (!existsSync10(this.plansDir)) {
|
|
41559
|
+
mkdirSync5(this.plansDir, { recursive: true });
|
|
40924
41560
|
}
|
|
40925
41561
|
}
|
|
40926
41562
|
slugify(name) {
|
|
@@ -41002,8 +41638,8 @@ IMPORTANT:
|
|
|
41002
41638
|
}
|
|
41003
41639
|
|
|
41004
41640
|
// ../sdk/src/planning/planning-meeting.ts
|
|
41005
|
-
import { existsSync as
|
|
41006
|
-
import { join as
|
|
41641
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10 } from "node:fs";
|
|
41642
|
+
import { join as join11 } from "node:path";
|
|
41007
41643
|
|
|
41008
41644
|
class PlanningMeeting {
|
|
41009
41645
|
projectPath;
|
|
@@ -41019,8 +41655,8 @@ class PlanningMeeting {
|
|
|
41019
41655
|
async run(directive, feedback) {
|
|
41020
41656
|
this.log("Planning sprint...", "info");
|
|
41021
41657
|
const plansDir = getLocusPath(this.projectPath, "plansDir");
|
|
41022
|
-
if (!
|
|
41023
|
-
|
|
41658
|
+
if (!existsSync11(plansDir)) {
|
|
41659
|
+
mkdirSync6(plansDir, { recursive: true });
|
|
41024
41660
|
}
|
|
41025
41661
|
const ts = Date.now();
|
|
41026
41662
|
const planId = `plan-${ts}`;
|
|
@@ -41034,11 +41670,11 @@ class PlanningMeeting {
|
|
|
41034
41670
|
});
|
|
41035
41671
|
const response = await this.aiRunner.run(prompt);
|
|
41036
41672
|
this.log("Planning meeting complete.", "success");
|
|
41037
|
-
const expectedPath =
|
|
41673
|
+
const expectedPath = join11(plansDir, `${fileName}.json`);
|
|
41038
41674
|
let plan = null;
|
|
41039
|
-
if (
|
|
41675
|
+
if (existsSync11(expectedPath)) {
|
|
41040
41676
|
try {
|
|
41041
|
-
plan = JSON.parse(
|
|
41677
|
+
plan = JSON.parse(readFileSync10(expectedPath, "utf-8"));
|
|
41042
41678
|
} catch {}
|
|
41043
41679
|
}
|
|
41044
41680
|
if (!plan) {
|
|
@@ -41073,112 +41709,13 @@ var init_index_node = __esm(() => {
|
|
|
41073
41709
|
init_agent2();
|
|
41074
41710
|
init_ai();
|
|
41075
41711
|
init_core3();
|
|
41712
|
+
init_discussion();
|
|
41076
41713
|
init_exec();
|
|
41077
41714
|
init_git();
|
|
41078
41715
|
init_src2();
|
|
41079
41716
|
init_planning();
|
|
41080
41717
|
});
|
|
41081
41718
|
|
|
41082
|
-
// src/display/execution-stats.ts
|
|
41083
|
-
class ExecutionStatsTracker {
|
|
41084
|
-
startTime;
|
|
41085
|
-
endTime = null;
|
|
41086
|
-
toolTimings = new Map;
|
|
41087
|
-
toolOrder = [];
|
|
41088
|
-
tokensUsed = null;
|
|
41089
|
-
error = null;
|
|
41090
|
-
constructor() {
|
|
41091
|
-
this.startTime = Date.now();
|
|
41092
|
-
}
|
|
41093
|
-
toolStarted(toolName, toolId) {
|
|
41094
|
-
const key = toolId ?? `${toolName}-${Date.now()}`;
|
|
41095
|
-
this.toolTimings.set(key, {
|
|
41096
|
-
name: toolName,
|
|
41097
|
-
id: toolId,
|
|
41098
|
-
startTime: Date.now()
|
|
41099
|
-
});
|
|
41100
|
-
this.toolOrder.push(key);
|
|
41101
|
-
}
|
|
41102
|
-
toolCompleted(toolName, toolId) {
|
|
41103
|
-
const key = this.findToolKey(toolName, toolId);
|
|
41104
|
-
if (key) {
|
|
41105
|
-
const timing = this.toolTimings.get(key);
|
|
41106
|
-
if (timing) {
|
|
41107
|
-
timing.endTime = Date.now();
|
|
41108
|
-
timing.duration = timing.endTime - timing.startTime;
|
|
41109
|
-
timing.success = true;
|
|
41110
|
-
}
|
|
41111
|
-
}
|
|
41112
|
-
}
|
|
41113
|
-
toolFailed(toolName, error48, toolId) {
|
|
41114
|
-
const key = this.findToolKey(toolName, toolId);
|
|
41115
|
-
if (key) {
|
|
41116
|
-
const timing = this.toolTimings.get(key);
|
|
41117
|
-
if (timing) {
|
|
41118
|
-
timing.endTime = Date.now();
|
|
41119
|
-
timing.duration = timing.endTime - timing.startTime;
|
|
41120
|
-
timing.success = false;
|
|
41121
|
-
timing.error = error48;
|
|
41122
|
-
}
|
|
41123
|
-
}
|
|
41124
|
-
}
|
|
41125
|
-
setTokensUsed(tokens) {
|
|
41126
|
-
this.tokensUsed = tokens;
|
|
41127
|
-
}
|
|
41128
|
-
setError(error48) {
|
|
41129
|
-
this.error = error48;
|
|
41130
|
-
}
|
|
41131
|
-
finalize() {
|
|
41132
|
-
this.endTime = Date.now();
|
|
41133
|
-
const toolsUsed = [];
|
|
41134
|
-
const seenTools = new Set;
|
|
41135
|
-
for (const key of this.toolOrder) {
|
|
41136
|
-
const timing = this.toolTimings.get(key);
|
|
41137
|
-
if (timing && !seenTools.has(timing.name)) {
|
|
41138
|
-
seenTools.add(timing.name);
|
|
41139
|
-
toolsUsed.push(timing.name);
|
|
41140
|
-
}
|
|
41141
|
-
}
|
|
41142
|
-
const toolTimings = this.toolOrder.map((key) => this.toolTimings.get(key)).filter((t) => t !== undefined);
|
|
41143
|
-
const stats = {
|
|
41144
|
-
duration: this.endTime - this.startTime,
|
|
41145
|
-
toolsUsed,
|
|
41146
|
-
toolTimings,
|
|
41147
|
-
success: this.error === null
|
|
41148
|
-
};
|
|
41149
|
-
if (this.tokensUsed !== null) {
|
|
41150
|
-
stats.tokensUsed = this.tokensUsed;
|
|
41151
|
-
}
|
|
41152
|
-
if (this.error !== null) {
|
|
41153
|
-
stats.error = this.error;
|
|
41154
|
-
}
|
|
41155
|
-
return stats;
|
|
41156
|
-
}
|
|
41157
|
-
getCurrentDuration() {
|
|
41158
|
-
return Date.now() - this.startTime;
|
|
41159
|
-
}
|
|
41160
|
-
findToolKey(toolName, toolId) {
|
|
41161
|
-
if (toolId && this.toolTimings.has(toolId)) {
|
|
41162
|
-
return toolId;
|
|
41163
|
-
}
|
|
41164
|
-
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
41165
|
-
const key = this.toolOrder[i];
|
|
41166
|
-
const timing = this.toolTimings.get(key);
|
|
41167
|
-
if (timing && timing.name === toolName && timing.endTime === undefined) {
|
|
41168
|
-
return key;
|
|
41169
|
-
}
|
|
41170
|
-
}
|
|
41171
|
-
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
41172
|
-
const key = this.toolOrder[i];
|
|
41173
|
-
const timing = this.toolTimings.get(key);
|
|
41174
|
-
if (timing && timing.name === toolName) {
|
|
41175
|
-
return key;
|
|
41176
|
-
}
|
|
41177
|
-
}
|
|
41178
|
-
return null;
|
|
41179
|
-
}
|
|
41180
|
-
}
|
|
41181
|
-
|
|
41182
41719
|
// src/display/tool-display.ts
|
|
41183
41720
|
import * as path2 from "node:path";
|
|
41184
41721
|
|
|
@@ -41769,6 +42306,106 @@ var init_progress_renderer = __esm(() => {
|
|
|
41769
42306
|
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
41770
42307
|
});
|
|
41771
42308
|
|
|
42309
|
+
// src/display/execution-stats.ts
|
|
42310
|
+
class ExecutionStatsTracker {
|
|
42311
|
+
startTime;
|
|
42312
|
+
endTime = null;
|
|
42313
|
+
toolTimings = new Map;
|
|
42314
|
+
toolOrder = [];
|
|
42315
|
+
tokensUsed = null;
|
|
42316
|
+
error = null;
|
|
42317
|
+
constructor() {
|
|
42318
|
+
this.startTime = Date.now();
|
|
42319
|
+
}
|
|
42320
|
+
toolStarted(toolName, toolId) {
|
|
42321
|
+
const key = toolId ?? `${toolName}-${Date.now()}`;
|
|
42322
|
+
this.toolTimings.set(key, {
|
|
42323
|
+
name: toolName,
|
|
42324
|
+
id: toolId,
|
|
42325
|
+
startTime: Date.now()
|
|
42326
|
+
});
|
|
42327
|
+
this.toolOrder.push(key);
|
|
42328
|
+
}
|
|
42329
|
+
toolCompleted(toolName, toolId) {
|
|
42330
|
+
const key = this.findToolKey(toolName, toolId);
|
|
42331
|
+
if (key) {
|
|
42332
|
+
const timing = this.toolTimings.get(key);
|
|
42333
|
+
if (timing) {
|
|
42334
|
+
timing.endTime = Date.now();
|
|
42335
|
+
timing.duration = timing.endTime - timing.startTime;
|
|
42336
|
+
timing.success = true;
|
|
42337
|
+
}
|
|
42338
|
+
}
|
|
42339
|
+
}
|
|
42340
|
+
toolFailed(toolName, error48, toolId) {
|
|
42341
|
+
const key = this.findToolKey(toolName, toolId);
|
|
42342
|
+
if (key) {
|
|
42343
|
+
const timing = this.toolTimings.get(key);
|
|
42344
|
+
if (timing) {
|
|
42345
|
+
timing.endTime = Date.now();
|
|
42346
|
+
timing.duration = timing.endTime - timing.startTime;
|
|
42347
|
+
timing.success = false;
|
|
42348
|
+
timing.error = error48;
|
|
42349
|
+
}
|
|
42350
|
+
}
|
|
42351
|
+
}
|
|
42352
|
+
setTokensUsed(tokens) {
|
|
42353
|
+
this.tokensUsed = tokens;
|
|
42354
|
+
}
|
|
42355
|
+
setError(error48) {
|
|
42356
|
+
this.error = error48;
|
|
42357
|
+
}
|
|
42358
|
+
finalize() {
|
|
42359
|
+
this.endTime = Date.now();
|
|
42360
|
+
const toolsUsed = [];
|
|
42361
|
+
const seenTools = new Set;
|
|
42362
|
+
for (const key of this.toolOrder) {
|
|
42363
|
+
const timing = this.toolTimings.get(key);
|
|
42364
|
+
if (timing && !seenTools.has(timing.name)) {
|
|
42365
|
+
seenTools.add(timing.name);
|
|
42366
|
+
toolsUsed.push(timing.name);
|
|
42367
|
+
}
|
|
42368
|
+
}
|
|
42369
|
+
const toolTimings = this.toolOrder.map((key) => this.toolTimings.get(key)).filter((t) => t !== undefined);
|
|
42370
|
+
const stats = {
|
|
42371
|
+
duration: this.endTime - this.startTime,
|
|
42372
|
+
toolsUsed,
|
|
42373
|
+
toolTimings,
|
|
42374
|
+
success: this.error === null
|
|
42375
|
+
};
|
|
42376
|
+
if (this.tokensUsed !== null) {
|
|
42377
|
+
stats.tokensUsed = this.tokensUsed;
|
|
42378
|
+
}
|
|
42379
|
+
if (this.error !== null) {
|
|
42380
|
+
stats.error = this.error;
|
|
42381
|
+
}
|
|
42382
|
+
return stats;
|
|
42383
|
+
}
|
|
42384
|
+
getCurrentDuration() {
|
|
42385
|
+
return Date.now() - this.startTime;
|
|
42386
|
+
}
|
|
42387
|
+
findToolKey(toolName, toolId) {
|
|
42388
|
+
if (toolId && this.toolTimings.has(toolId)) {
|
|
42389
|
+
return toolId;
|
|
42390
|
+
}
|
|
42391
|
+
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42392
|
+
const key = this.toolOrder[i];
|
|
42393
|
+
const timing = this.toolTimings.get(key);
|
|
42394
|
+
if (timing && timing.name === toolName && timing.endTime === undefined) {
|
|
42395
|
+
return key;
|
|
42396
|
+
}
|
|
42397
|
+
}
|
|
42398
|
+
for (let i = this.toolOrder.length - 1;i >= 0; i--) {
|
|
42399
|
+
const key = this.toolOrder[i];
|
|
42400
|
+
const timing = this.toolTimings.get(key);
|
|
42401
|
+
if (timing && timing.name === toolName) {
|
|
42402
|
+
return key;
|
|
42403
|
+
}
|
|
42404
|
+
}
|
|
42405
|
+
return null;
|
|
42406
|
+
}
|
|
42407
|
+
}
|
|
42408
|
+
|
|
41772
42409
|
// src/repl/commands.ts
|
|
41773
42410
|
function parseCommand(input) {
|
|
41774
42411
|
const lowerInput = input.toLowerCase();
|
|
@@ -41878,7 +42515,7 @@ var exports_interactive_session = {};
|
|
|
41878
42515
|
__export(exports_interactive_session, {
|
|
41879
42516
|
InteractiveSession: () => InteractiveSession
|
|
41880
42517
|
});
|
|
41881
|
-
import * as
|
|
42518
|
+
import * as readline2 from "node:readline";
|
|
41882
42519
|
|
|
41883
42520
|
class InteractiveSession {
|
|
41884
42521
|
readline = null;
|
|
@@ -41924,7 +42561,7 @@ class InteractiveSession {
|
|
|
41924
42561
|
}
|
|
41925
42562
|
async start() {
|
|
41926
42563
|
this.printWelcome();
|
|
41927
|
-
this.readline =
|
|
42564
|
+
this.readline = readline2.createInterface({
|
|
41928
42565
|
input: process.stdin,
|
|
41929
42566
|
output: process.stdout,
|
|
41930
42567
|
terminal: true
|
|
@@ -42121,10 +42758,10 @@ import { createInterface } from "node:readline";
|
|
|
42121
42758
|
|
|
42122
42759
|
// src/settings-manager.ts
|
|
42123
42760
|
init_index_node();
|
|
42124
|
-
import { existsSync as
|
|
42125
|
-
import { join as
|
|
42761
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
42762
|
+
import { join as join12 } from "node:path";
|
|
42126
42763
|
function getSettingsPath(projectPath) {
|
|
42127
|
-
return
|
|
42764
|
+
return join12(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
42128
42765
|
}
|
|
42129
42766
|
|
|
42130
42767
|
class SettingsManager {
|
|
@@ -42134,16 +42771,16 @@ class SettingsManager {
|
|
|
42134
42771
|
}
|
|
42135
42772
|
load() {
|
|
42136
42773
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42137
|
-
if (!
|
|
42774
|
+
if (!existsSync12(settingsPath)) {
|
|
42138
42775
|
return {};
|
|
42139
42776
|
}
|
|
42140
|
-
return JSON.parse(
|
|
42777
|
+
return JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
42141
42778
|
}
|
|
42142
42779
|
save(settings) {
|
|
42143
42780
|
const { $schema: _2, ...rest } = settings;
|
|
42144
42781
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
42145
42782
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42146
|
-
|
|
42783
|
+
writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
42147
42784
|
}
|
|
42148
42785
|
get(key) {
|
|
42149
42786
|
return this.load()[key];
|
|
@@ -42155,12 +42792,12 @@ class SettingsManager {
|
|
|
42155
42792
|
}
|
|
42156
42793
|
remove() {
|
|
42157
42794
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42158
|
-
if (
|
|
42159
|
-
|
|
42795
|
+
if (existsSync12(settingsPath)) {
|
|
42796
|
+
unlinkSync4(settingsPath);
|
|
42160
42797
|
}
|
|
42161
42798
|
}
|
|
42162
42799
|
exists() {
|
|
42163
|
-
return
|
|
42800
|
+
return existsSync12(getSettingsPath(this.projectPath));
|
|
42164
42801
|
}
|
|
42165
42802
|
}
|
|
42166
42803
|
|
|
@@ -42419,15 +43056,444 @@ async function configCommand(args) {
|
|
|
42419
43056
|
showConfigHelp();
|
|
42420
43057
|
}
|
|
42421
43058
|
}
|
|
42422
|
-
// src/commands/
|
|
43059
|
+
// src/commands/discuss.ts
|
|
42423
43060
|
init_index_node();
|
|
43061
|
+
init_progress_renderer();
|
|
43062
|
+
import * as readline from "node:readline";
|
|
42424
43063
|
import { parseArgs } from "node:util";
|
|
42425
43064
|
|
|
43065
|
+
// src/utils/banner.ts
|
|
43066
|
+
init_index_node();
|
|
43067
|
+
|
|
43068
|
+
// src/utils/version.ts
|
|
43069
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
|
|
43070
|
+
import { dirname as dirname3, join as join13 } from "node:path";
|
|
43071
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43072
|
+
function getVersion() {
|
|
43073
|
+
try {
|
|
43074
|
+
const __filename2 = fileURLToPath3(import.meta.url);
|
|
43075
|
+
const __dirname2 = dirname3(__filename2);
|
|
43076
|
+
const bundledPath = join13(__dirname2, "..", "package.json");
|
|
43077
|
+
const sourcePath = join13(__dirname2, "..", "..", "package.json");
|
|
43078
|
+
if (existsSync13(bundledPath)) {
|
|
43079
|
+
const pkg = JSON.parse(readFileSync12(bundledPath, "utf-8"));
|
|
43080
|
+
if (pkg.name === "@locusai/cli") {
|
|
43081
|
+
return pkg.version || "0.0.0";
|
|
43082
|
+
}
|
|
43083
|
+
}
|
|
43084
|
+
if (existsSync13(sourcePath)) {
|
|
43085
|
+
const pkg = JSON.parse(readFileSync12(sourcePath, "utf-8"));
|
|
43086
|
+
if (pkg.name === "@locusai/cli") {
|
|
43087
|
+
return pkg.version || "0.0.0";
|
|
43088
|
+
}
|
|
43089
|
+
}
|
|
43090
|
+
} catch {}
|
|
43091
|
+
return "0.0.0";
|
|
43092
|
+
}
|
|
43093
|
+
var VERSION2 = getVersion();
|
|
43094
|
+
|
|
43095
|
+
// src/utils/banner.ts
|
|
43096
|
+
function printBanner() {
|
|
43097
|
+
console.log(c.primary(`
|
|
43098
|
+
_ ____ ____ _ _ ____
|
|
43099
|
+
| | / __ \\ / ___| | | |/ ___|
|
|
43100
|
+
| | | | | | | | | | |\\___ \\
|
|
43101
|
+
| |___| |__| | |___| |_| |___) |
|
|
43102
|
+
|_____|\\____/ \\____|\\___/|____/ ${c.dim(`v${VERSION2}`)}
|
|
43103
|
+
`));
|
|
43104
|
+
}
|
|
43105
|
+
// src/utils/helpers.ts
|
|
43106
|
+
init_index_node();
|
|
43107
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
43108
|
+
import { join as join14 } from "node:path";
|
|
43109
|
+
function isProjectInitialized(projectPath) {
|
|
43110
|
+
const locusDir = join14(projectPath, LOCUS_CONFIG.dir);
|
|
43111
|
+
const configPath = join14(locusDir, LOCUS_CONFIG.configFile);
|
|
43112
|
+
return existsSync14(locusDir) && existsSync14(configPath);
|
|
43113
|
+
}
|
|
43114
|
+
function requireInitialization(projectPath, command) {
|
|
43115
|
+
if (!isProjectInitialized(projectPath)) {
|
|
43116
|
+
console.error(`
|
|
43117
|
+
${c.error("✖ Error")} ${c.red(`Locus is not initialized in this directory.`)}
|
|
43118
|
+
|
|
43119
|
+
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
43120
|
+
|
|
43121
|
+
To initialize Locus in this directory, run:
|
|
43122
|
+
${c.primary("locus init")}
|
|
43123
|
+
|
|
43124
|
+
This will create a ${c.dim(".locus")} directory with the necessary configuration.
|
|
43125
|
+
`);
|
|
43126
|
+
process.exit(1);
|
|
43127
|
+
}
|
|
43128
|
+
}
|
|
43129
|
+
function resolveProvider3(input) {
|
|
43130
|
+
if (!input)
|
|
43131
|
+
return PROVIDER.CLAUDE;
|
|
43132
|
+
if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
|
|
43133
|
+
return input;
|
|
43134
|
+
console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
|
|
43135
|
+
process.exit(1);
|
|
43136
|
+
}
|
|
43137
|
+
// src/commands/discuss.ts
|
|
43138
|
+
async function discussCommand(args) {
|
|
43139
|
+
const { values, positionals } = parseArgs({
|
|
43140
|
+
args,
|
|
43141
|
+
options: {
|
|
43142
|
+
list: { type: "boolean" },
|
|
43143
|
+
show: { type: "string" },
|
|
43144
|
+
archive: { type: "string" },
|
|
43145
|
+
delete: { type: "string" },
|
|
43146
|
+
model: { type: "string" },
|
|
43147
|
+
provider: { type: "string" },
|
|
43148
|
+
"reasoning-effort": { type: "string" },
|
|
43149
|
+
dir: { type: "string" }
|
|
43150
|
+
},
|
|
43151
|
+
strict: false,
|
|
43152
|
+
allowPositionals: true
|
|
43153
|
+
});
|
|
43154
|
+
const projectPath = values.dir || process.cwd();
|
|
43155
|
+
requireInitialization(projectPath, "discuss");
|
|
43156
|
+
const discussionManager = new DiscussionManager(projectPath);
|
|
43157
|
+
if (values.list) {
|
|
43158
|
+
return listDiscussions(discussionManager);
|
|
43159
|
+
}
|
|
43160
|
+
if (values.show) {
|
|
43161
|
+
return showDiscussion(discussionManager, values.show);
|
|
43162
|
+
}
|
|
43163
|
+
if (values.archive) {
|
|
43164
|
+
return archiveDiscussion(discussionManager, values.archive);
|
|
43165
|
+
}
|
|
43166
|
+
if (values.delete) {
|
|
43167
|
+
return deleteDiscussion(discussionManager, values.delete);
|
|
43168
|
+
}
|
|
43169
|
+
const topic = positionals.join(" ").trim();
|
|
43170
|
+
if (!topic) {
|
|
43171
|
+
showDiscussHelp();
|
|
43172
|
+
return;
|
|
43173
|
+
}
|
|
43174
|
+
const settings = new SettingsManager(projectPath).load();
|
|
43175
|
+
const provider = resolveProvider3(values.provider || settings.provider);
|
|
43176
|
+
const model = values.model || settings.model || DEFAULT_MODEL[provider];
|
|
43177
|
+
const reasoningEffort = values["reasoning-effort"];
|
|
43178
|
+
const aiRunner = createAiRunner(provider, {
|
|
43179
|
+
projectPath,
|
|
43180
|
+
model,
|
|
43181
|
+
reasoningEffort
|
|
43182
|
+
});
|
|
43183
|
+
const log = (message, level) => {
|
|
43184
|
+
const icon = level === "success" ? c.success("✔") : level === "error" ? c.error("✖") : level === "warn" ? c.warning("!") : c.info("●");
|
|
43185
|
+
console.log(` ${icon} ${message}`);
|
|
43186
|
+
};
|
|
43187
|
+
const facilitator = new DiscussionFacilitator({
|
|
43188
|
+
projectPath,
|
|
43189
|
+
aiRunner,
|
|
43190
|
+
discussionManager,
|
|
43191
|
+
log,
|
|
43192
|
+
provider,
|
|
43193
|
+
model
|
|
43194
|
+
});
|
|
43195
|
+
console.log(`
|
|
43196
|
+
${c.header(" DISCUSSION ")} ${c.bold("Starting interactive discussion...")}
|
|
43197
|
+
`);
|
|
43198
|
+
console.log(` ${c.dim("Topic:")} ${c.bold(topic)}`);
|
|
43199
|
+
console.log(` ${c.dim("Model:")} ${c.dim(`${model} (${provider})`)}
|
|
43200
|
+
`);
|
|
43201
|
+
const renderer = new ProgressRenderer({ animated: true });
|
|
43202
|
+
let discussionId;
|
|
43203
|
+
try {
|
|
43204
|
+
renderer.showThinkingStarted();
|
|
43205
|
+
const result = await facilitator.startDiscussion(topic);
|
|
43206
|
+
renderer.showThinkingStopped();
|
|
43207
|
+
discussionId = result.discussion.id;
|
|
43208
|
+
process.stdout.write(`
|
|
43209
|
+
`);
|
|
43210
|
+
process.stdout.write(result.message);
|
|
43211
|
+
process.stdout.write(`
|
|
43212
|
+
|
|
43213
|
+
`);
|
|
43214
|
+
renderer.finalize();
|
|
43215
|
+
} catch (error48) {
|
|
43216
|
+
renderer.finalize();
|
|
43217
|
+
console.error(`
|
|
43218
|
+
${c.error("✖")} ${c.red("Failed to start discussion:")} ${error48 instanceof Error ? error48.message : String(error48)}
|
|
43219
|
+
`);
|
|
43220
|
+
process.exit(1);
|
|
43221
|
+
}
|
|
43222
|
+
console.log(` ${c.dim("Type your response, or 'help' for commands. Use 'exit' or Ctrl+C to quit.")}
|
|
43223
|
+
`);
|
|
43224
|
+
const rl = readline.createInterface({
|
|
43225
|
+
input: process.stdin,
|
|
43226
|
+
output: process.stdout,
|
|
43227
|
+
terminal: true
|
|
43228
|
+
});
|
|
43229
|
+
rl.setPrompt(c.cyan("> "));
|
|
43230
|
+
rl.prompt();
|
|
43231
|
+
let isProcessing = false;
|
|
43232
|
+
const shutdown = () => {
|
|
43233
|
+
if (isProcessing) {
|
|
43234
|
+
aiRunner.abort();
|
|
43235
|
+
}
|
|
43236
|
+
console.log(`
|
|
43237
|
+
${c.dim("Discussion saved.")} ${c.dim("ID:")} ${c.cyan(discussionId)}`);
|
|
43238
|
+
console.log(c.dim(`
|
|
43239
|
+
Goodbye!
|
|
43240
|
+
`));
|
|
43241
|
+
rl.close();
|
|
43242
|
+
process.exit(0);
|
|
43243
|
+
};
|
|
43244
|
+
process.on("SIGINT", () => {
|
|
43245
|
+
if (isProcessing) {
|
|
43246
|
+
aiRunner.abort();
|
|
43247
|
+
isProcessing = false;
|
|
43248
|
+
console.log(c.dim(`
|
|
43249
|
+
[Interrupted]`));
|
|
43250
|
+
rl.prompt();
|
|
43251
|
+
} else {
|
|
43252
|
+
shutdown();
|
|
43253
|
+
}
|
|
43254
|
+
});
|
|
43255
|
+
rl.on("close", () => {
|
|
43256
|
+
shutdown();
|
|
43257
|
+
});
|
|
43258
|
+
rl.on("line", async (input) => {
|
|
43259
|
+
const trimmed = input.trim();
|
|
43260
|
+
if (trimmed === "" || isProcessing) {
|
|
43261
|
+
rl.prompt();
|
|
43262
|
+
return;
|
|
43263
|
+
}
|
|
43264
|
+
const lowerInput = trimmed.toLowerCase();
|
|
43265
|
+
if (lowerInput === "help") {
|
|
43266
|
+
showReplHelp();
|
|
43267
|
+
rl.prompt();
|
|
43268
|
+
return;
|
|
43269
|
+
}
|
|
43270
|
+
if (lowerInput === "exit" || lowerInput === "quit") {
|
|
43271
|
+
shutdown();
|
|
43272
|
+
return;
|
|
43273
|
+
}
|
|
43274
|
+
if (lowerInput === "insights") {
|
|
43275
|
+
showCurrentInsights(discussionManager, discussionId);
|
|
43276
|
+
rl.prompt();
|
|
43277
|
+
return;
|
|
43278
|
+
}
|
|
43279
|
+
if (lowerInput === "summary") {
|
|
43280
|
+
isProcessing = true;
|
|
43281
|
+
const summaryRenderer = new ProgressRenderer({ animated: true });
|
|
43282
|
+
try {
|
|
43283
|
+
summaryRenderer.showThinkingStarted();
|
|
43284
|
+
const summary = await facilitator.summarizeDiscussion(discussionId);
|
|
43285
|
+
summaryRenderer.showThinkingStopped();
|
|
43286
|
+
process.stdout.write(`
|
|
43287
|
+
`);
|
|
43288
|
+
process.stdout.write(summary);
|
|
43289
|
+
process.stdout.write(`
|
|
43290
|
+
`);
|
|
43291
|
+
summaryRenderer.finalize();
|
|
43292
|
+
const discussion2 = discussionManager.load(discussionId);
|
|
43293
|
+
if (discussion2) {
|
|
43294
|
+
console.log(`
|
|
43295
|
+
${c.success("✔")} ${c.success("Discussion completed!")}
|
|
43296
|
+
`);
|
|
43297
|
+
console.log(` ${c.dim("Messages:")} ${discussion2.messages.length} ${c.dim("Insights:")} ${discussion2.insights.length}
|
|
43298
|
+
`);
|
|
43299
|
+
}
|
|
43300
|
+
console.log(` ${c.dim("To review:")} ${c.cyan(`locus discuss --show ${discussionId}`)}`);
|
|
43301
|
+
console.log(` ${c.dim("To list all:")} ${c.cyan("locus discuss --list")}
|
|
43302
|
+
`);
|
|
43303
|
+
} catch (error48) {
|
|
43304
|
+
summaryRenderer.finalize();
|
|
43305
|
+
console.error(`
|
|
43306
|
+
${c.error("✖")} ${c.red("Failed to summarize:")} ${error48 instanceof Error ? error48.message : String(error48)}
|
|
43307
|
+
`);
|
|
43308
|
+
}
|
|
43309
|
+
rl.close();
|
|
43310
|
+
process.exit(0);
|
|
43311
|
+
return;
|
|
43312
|
+
}
|
|
43313
|
+
isProcessing = true;
|
|
43314
|
+
const chunkRenderer = new ProgressRenderer({ animated: true });
|
|
43315
|
+
try {
|
|
43316
|
+
chunkRenderer.showThinkingStarted();
|
|
43317
|
+
const stream4 = facilitator.continueDiscussionStream(discussionId, trimmed);
|
|
43318
|
+
let result = {
|
|
43319
|
+
response: "",
|
|
43320
|
+
insights: []
|
|
43321
|
+
};
|
|
43322
|
+
let iterResult = await stream4.next();
|
|
43323
|
+
while (!iterResult.done) {
|
|
43324
|
+
chunkRenderer.renderChunk(iterResult.value);
|
|
43325
|
+
iterResult = await stream4.next();
|
|
43326
|
+
}
|
|
43327
|
+
result = iterResult.value;
|
|
43328
|
+
chunkRenderer.finalize();
|
|
43329
|
+
if (result.insights.length > 0) {
|
|
43330
|
+
console.log("");
|
|
43331
|
+
for (const insight of result.insights) {
|
|
43332
|
+
const tag = formatInsightTag(insight.type);
|
|
43333
|
+
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
43334
|
+
console.log(` ${c.dim(insight.content)}
|
|
43335
|
+
`);
|
|
43336
|
+
}
|
|
43337
|
+
}
|
|
43338
|
+
} catch (error48) {
|
|
43339
|
+
chunkRenderer.finalize();
|
|
43340
|
+
console.error(`
|
|
43341
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
43342
|
+
`);
|
|
43343
|
+
}
|
|
43344
|
+
isProcessing = false;
|
|
43345
|
+
rl.prompt();
|
|
43346
|
+
});
|
|
43347
|
+
}
|
|
43348
|
+
function listDiscussions(discussionManager) {
|
|
43349
|
+
const discussions = discussionManager.list();
|
|
43350
|
+
if (discussions.length === 0) {
|
|
43351
|
+
console.log(`
|
|
43352
|
+
${c.dim("No discussions found.")}
|
|
43353
|
+
`);
|
|
43354
|
+
console.log(` ${c.dim("Start one with:")} ${c.cyan('locus discuss "your topic"')}
|
|
43355
|
+
`);
|
|
43356
|
+
return;
|
|
43357
|
+
}
|
|
43358
|
+
console.log(`
|
|
43359
|
+
${c.header(" DISCUSSIONS ")} ${c.dim(`(${discussions.length})`)}
|
|
43360
|
+
`);
|
|
43361
|
+
for (const disc of discussions) {
|
|
43362
|
+
const statusIcon = disc.status === "active" ? c.warning("◯") : disc.status === "completed" ? c.success("✔") : c.dim("⊘");
|
|
43363
|
+
console.log(` ${statusIcon} ${c.bold(disc.title)} ${c.dim(`[${disc.status}]`)} ${c.dim(`— ${disc.messages.length} messages, ${disc.insights.length} insights`)}`);
|
|
43364
|
+
console.log(` ${c.dim("ID:")} ${disc.id}`);
|
|
43365
|
+
console.log(` ${c.dim("Created:")} ${disc.createdAt}`);
|
|
43366
|
+
console.log("");
|
|
43367
|
+
}
|
|
43368
|
+
}
|
|
43369
|
+
function showDiscussion(discussionManager, id) {
|
|
43370
|
+
const md = discussionManager.getMarkdown(id);
|
|
43371
|
+
if (!md) {
|
|
43372
|
+
console.error(`
|
|
43373
|
+
${c.error("✖")} ${c.red(`Discussion not found: ${id}`)}
|
|
43374
|
+
`);
|
|
43375
|
+
process.exit(1);
|
|
43376
|
+
}
|
|
43377
|
+
console.log(`
|
|
43378
|
+
${md}
|
|
43379
|
+
`);
|
|
43380
|
+
}
|
|
43381
|
+
function archiveDiscussion(discussionManager, id) {
|
|
43382
|
+
try {
|
|
43383
|
+
discussionManager.archive(id);
|
|
43384
|
+
console.log(`
|
|
43385
|
+
${c.success("✔")} ${c.dim("Discussion archived.")}
|
|
43386
|
+
`);
|
|
43387
|
+
} catch (error48) {
|
|
43388
|
+
console.error(`
|
|
43389
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
43390
|
+
`);
|
|
43391
|
+
process.exit(1);
|
|
43392
|
+
}
|
|
43393
|
+
}
|
|
43394
|
+
function deleteDiscussion(discussionManager, id) {
|
|
43395
|
+
try {
|
|
43396
|
+
discussionManager.delete(id);
|
|
43397
|
+
console.log(`
|
|
43398
|
+
${c.success("✔")} ${c.dim("Discussion deleted.")}
|
|
43399
|
+
`);
|
|
43400
|
+
} catch (error48) {
|
|
43401
|
+
console.error(`
|
|
43402
|
+
${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
|
|
43403
|
+
`);
|
|
43404
|
+
process.exit(1);
|
|
43405
|
+
}
|
|
43406
|
+
}
|
|
43407
|
+
function showCurrentInsights(discussionManager, discussionId) {
|
|
43408
|
+
const discussion2 = discussionManager.load(discussionId);
|
|
43409
|
+
if (!discussion2 || discussion2.insights.length === 0) {
|
|
43410
|
+
console.log(`
|
|
43411
|
+
${c.dim("No insights extracted yet.")}
|
|
43412
|
+
`);
|
|
43413
|
+
return;
|
|
43414
|
+
}
|
|
43415
|
+
console.log(`
|
|
43416
|
+
${c.header(" INSIGHTS ")} ${c.dim(`(${discussion2.insights.length})`)}
|
|
43417
|
+
`);
|
|
43418
|
+
for (const insight of discussion2.insights) {
|
|
43419
|
+
const tag = formatInsightTag(insight.type);
|
|
43420
|
+
console.log(` ${tag} ${c.bold(insight.title)}`);
|
|
43421
|
+
console.log(` ${c.dim(insight.content)}`);
|
|
43422
|
+
if (insight.tags.length > 0) {
|
|
43423
|
+
console.log(` ${c.dim(`Tags: ${insight.tags.join(", ")}`)}`);
|
|
43424
|
+
}
|
|
43425
|
+
console.log("");
|
|
43426
|
+
}
|
|
43427
|
+
}
|
|
43428
|
+
function formatInsightTag(type) {
|
|
43429
|
+
switch (type) {
|
|
43430
|
+
case "decision":
|
|
43431
|
+
return c.green("[DECISION]");
|
|
43432
|
+
case "requirement":
|
|
43433
|
+
return c.blue("[REQUIREMENT]");
|
|
43434
|
+
case "idea":
|
|
43435
|
+
return c.yellow("[IDEA]");
|
|
43436
|
+
case "concern":
|
|
43437
|
+
return c.red("[CONCERN]");
|
|
43438
|
+
case "learning":
|
|
43439
|
+
return c.cyan("[LEARNING]");
|
|
43440
|
+
}
|
|
43441
|
+
}
|
|
43442
|
+
function showReplHelp() {
|
|
43443
|
+
console.log(`
|
|
43444
|
+
${c.header(" DISCUSSION COMMANDS ")}
|
|
43445
|
+
|
|
43446
|
+
${c.cyan("summary")} Generate a final summary and end the discussion
|
|
43447
|
+
${c.cyan("insights")} Show all insights extracted so far
|
|
43448
|
+
${c.cyan("exit")} Save and exit without generating a summary
|
|
43449
|
+
${c.cyan("help")} Show this help message
|
|
43450
|
+
|
|
43451
|
+
${c.dim("Type anything else to continue the discussion.")}
|
|
43452
|
+
`);
|
|
43453
|
+
}
|
|
43454
|
+
function showDiscussHelp() {
|
|
43455
|
+
console.log(`
|
|
43456
|
+
${c.header(" LOCUS DISCUSS ")} ${c.dim("— Interactive AI Discussion")}
|
|
43457
|
+
|
|
43458
|
+
${c.bold("Usage:")}
|
|
43459
|
+
${c.cyan('locus discuss "topic"')} Start a discussion on a topic
|
|
43460
|
+
${c.cyan("locus discuss --list")} List all discussions
|
|
43461
|
+
${c.cyan("locus discuss --show <id>")} Show discussion details
|
|
43462
|
+
${c.cyan("locus discuss --archive <id>")} Archive a discussion
|
|
43463
|
+
${c.cyan("locus discuss --delete <id>")} Delete a discussion
|
|
43464
|
+
|
|
43465
|
+
${c.bold("Options:")}
|
|
43466
|
+
${c.dim("--model <model>")} AI model to use
|
|
43467
|
+
${c.dim("--provider <p>")} AI provider (claude, codex)
|
|
43468
|
+
${c.dim("--reasoning-effort <level>")} Reasoning effort (low, medium, high)
|
|
43469
|
+
${c.dim("--dir <path>")} Project directory
|
|
43470
|
+
|
|
43471
|
+
${c.bold("REPL Commands:")}
|
|
43472
|
+
${c.dim("summary")} Generate final summary and end the discussion
|
|
43473
|
+
${c.dim("insights")} Show all insights extracted so far
|
|
43474
|
+
${c.dim("exit")} Save and exit without generating a summary
|
|
43475
|
+
${c.dim("help")} Show available commands
|
|
43476
|
+
|
|
43477
|
+
${c.bold("Examples:")}
|
|
43478
|
+
${c.dim("# Start a discussion about architecture")}
|
|
43479
|
+
${c.cyan('locus discuss "how should we structure the auth system?"')}
|
|
43480
|
+
|
|
43481
|
+
${c.dim("# Review a past discussion")}
|
|
43482
|
+
${c.cyan("locus discuss --show disc-1234567890")}
|
|
43483
|
+
|
|
43484
|
+
${c.dim("# List all discussions")}
|
|
43485
|
+
${c.cyan("locus discuss --list")}
|
|
43486
|
+
`);
|
|
43487
|
+
}
|
|
43488
|
+
// src/commands/docs.ts
|
|
43489
|
+
init_index_node();
|
|
43490
|
+
import { parseArgs as parseArgs2 } from "node:util";
|
|
43491
|
+
|
|
42426
43492
|
// src/config-manager.ts
|
|
42427
43493
|
init_index_node();
|
|
42428
43494
|
import { execSync as execSync2 } from "node:child_process";
|
|
42429
|
-
import { existsSync as
|
|
42430
|
-
import { join as
|
|
43495
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "node:fs";
|
|
43496
|
+
import { join as join15 } from "node:path";
|
|
42431
43497
|
var LOCUS_GITIGNORE_MARKER = "# Locus AI";
|
|
42432
43498
|
var LOCUS_MD_TEMPLATE = `## Planning First
|
|
42433
43499
|
|
|
@@ -42519,12 +43585,12 @@ It is read by AI agents before every task to avoid repeating mistakes and to fol
|
|
|
42519
43585
|
<!-- Add learnings below this line. Format: - **[Category]**: Description -->
|
|
42520
43586
|
`;
|
|
42521
43587
|
function updateGitignore(projectPath) {
|
|
42522
|
-
const gitignorePath =
|
|
43588
|
+
const gitignorePath = join15(projectPath, ".gitignore");
|
|
42523
43589
|
let content = "";
|
|
42524
43590
|
const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
|
|
42525
43591
|
`);
|
|
42526
|
-
if (
|
|
42527
|
-
content =
|
|
43592
|
+
if (existsSync15(gitignorePath)) {
|
|
43593
|
+
content = readFileSync13(gitignorePath, "utf-8");
|
|
42528
43594
|
if (content.includes(LOCUS_GITIGNORE_MARKER)) {
|
|
42529
43595
|
const lines = content.split(`
|
|
42530
43596
|
`);
|
|
@@ -42541,7 +43607,7 @@ function updateGitignore(projectPath) {
|
|
|
42541
43607
|
const after = lines.slice(endIdx + 1);
|
|
42542
43608
|
content = [...before, locusBlock, ...after].join(`
|
|
42543
43609
|
`);
|
|
42544
|
-
|
|
43610
|
+
writeFileSync7(gitignorePath, content);
|
|
42545
43611
|
return;
|
|
42546
43612
|
}
|
|
42547
43613
|
if (content.length > 0 && !content.endsWith(`
|
|
@@ -42556,7 +43622,7 @@ function updateGitignore(projectPath) {
|
|
|
42556
43622
|
}
|
|
42557
43623
|
content += `${locusBlock}
|
|
42558
43624
|
`;
|
|
42559
|
-
|
|
43625
|
+
writeFileSync7(gitignorePath, content);
|
|
42560
43626
|
}
|
|
42561
43627
|
function ensureGitIdentity(projectPath) {
|
|
42562
43628
|
const hasName = (() => {
|
|
@@ -42603,48 +43669,49 @@ class ConfigManager {
|
|
|
42603
43669
|
this.projectPath = projectPath;
|
|
42604
43670
|
}
|
|
42605
43671
|
async init(version2) {
|
|
42606
|
-
const locusConfigDir =
|
|
43672
|
+
const locusConfigDir = join15(this.projectPath, LOCUS_CONFIG.dir);
|
|
42607
43673
|
const locusConfigPath = getLocusPath(this.projectPath, "configFile");
|
|
42608
|
-
if (!
|
|
42609
|
-
|
|
43674
|
+
if (!existsSync15(locusConfigDir)) {
|
|
43675
|
+
mkdirSync7(locusConfigDir, { recursive: true });
|
|
42610
43676
|
}
|
|
42611
43677
|
const locusSubdirs = [
|
|
42612
43678
|
LOCUS_CONFIG.artifactsDir,
|
|
42613
43679
|
LOCUS_CONFIG.documentsDir,
|
|
42614
43680
|
LOCUS_CONFIG.sessionsDir,
|
|
42615
43681
|
LOCUS_CONFIG.reviewsDir,
|
|
42616
|
-
LOCUS_CONFIG.plansDir
|
|
43682
|
+
LOCUS_CONFIG.plansDir,
|
|
43683
|
+
LOCUS_CONFIG.discussionsDir
|
|
42617
43684
|
];
|
|
42618
43685
|
for (const subdir of locusSubdirs) {
|
|
42619
|
-
const subdirPath =
|
|
42620
|
-
if (!
|
|
42621
|
-
|
|
43686
|
+
const subdirPath = join15(locusConfigDir, subdir);
|
|
43687
|
+
if (!existsSync15(subdirPath)) {
|
|
43688
|
+
mkdirSync7(subdirPath, { recursive: true });
|
|
42622
43689
|
}
|
|
42623
43690
|
}
|
|
42624
43691
|
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
42625
|
-
if (!
|
|
42626
|
-
|
|
43692
|
+
if (!existsSync15(locusMdPath)) {
|
|
43693
|
+
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
42627
43694
|
}
|
|
42628
43695
|
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42629
|
-
if (!
|
|
42630
|
-
|
|
43696
|
+
if (!existsSync15(learningsMdPath)) {
|
|
43697
|
+
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
42631
43698
|
}
|
|
42632
|
-
if (!
|
|
43699
|
+
if (!existsSync15(locusConfigPath)) {
|
|
42633
43700
|
const config2 = {
|
|
42634
43701
|
$schema: LOCUS_SCHEMAS.config,
|
|
42635
43702
|
version: version2,
|
|
42636
43703
|
createdAt: new Date().toISOString(),
|
|
42637
43704
|
projectPath: "."
|
|
42638
43705
|
};
|
|
42639
|
-
|
|
43706
|
+
writeFileSync7(locusConfigPath, JSON.stringify(config2, null, 2));
|
|
42640
43707
|
}
|
|
42641
43708
|
updateGitignore(this.projectPath);
|
|
42642
43709
|
ensureGitIdentity(this.projectPath);
|
|
42643
43710
|
}
|
|
42644
43711
|
loadConfig() {
|
|
42645
|
-
const
|
|
42646
|
-
if (
|
|
42647
|
-
return JSON.parse(
|
|
43712
|
+
const path3 = getLocusPath(this.projectPath, "configFile");
|
|
43713
|
+
if (existsSync15(path3)) {
|
|
43714
|
+
return JSON.parse(readFileSync13(path3, "utf-8"));
|
|
42648
43715
|
}
|
|
42649
43716
|
return null;
|
|
42650
43717
|
}
|
|
@@ -42674,19 +43741,19 @@ class ConfigManager {
|
|
|
42674
43741
|
this.saveConfig(config2);
|
|
42675
43742
|
}
|
|
42676
43743
|
}
|
|
42677
|
-
const settingsPath =
|
|
42678
|
-
if (
|
|
42679
|
-
const raw =
|
|
43744
|
+
const settingsPath = join15(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
43745
|
+
if (existsSync15(settingsPath)) {
|
|
43746
|
+
const raw = readFileSync13(settingsPath, "utf-8");
|
|
42680
43747
|
const settings = JSON.parse(raw);
|
|
42681
43748
|
if (settings.$schema !== LOCUS_SCHEMAS.settings) {
|
|
42682
43749
|
const { $schema: _2, ...rest } = settings;
|
|
42683
43750
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
42684
|
-
|
|
43751
|
+
writeFileSync7(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
42685
43752
|
}
|
|
42686
43753
|
}
|
|
42687
43754
|
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
42688
|
-
const locusMdExisted =
|
|
42689
|
-
|
|
43755
|
+
const locusMdExisted = existsSync15(locusMdPath);
|
|
43756
|
+
writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
42690
43757
|
if (!locusMdExisted) {
|
|
42691
43758
|
result.directoriesCreated.push(".locus/LOCUS.md");
|
|
42692
43759
|
}
|
|
@@ -42695,25 +43762,26 @@ class ConfigManager {
|
|
|
42695
43762
|
LOCUS_CONFIG.documentsDir,
|
|
42696
43763
|
LOCUS_CONFIG.sessionsDir,
|
|
42697
43764
|
LOCUS_CONFIG.reviewsDir,
|
|
42698
|
-
LOCUS_CONFIG.plansDir
|
|
43765
|
+
LOCUS_CONFIG.plansDir,
|
|
43766
|
+
LOCUS_CONFIG.discussionsDir
|
|
42699
43767
|
];
|
|
42700
|
-
const locusConfigDir =
|
|
43768
|
+
const locusConfigDir = join15(this.projectPath, LOCUS_CONFIG.dir);
|
|
42701
43769
|
for (const subdir of locusSubdirs) {
|
|
42702
|
-
const subdirPath =
|
|
42703
|
-
if (!
|
|
42704
|
-
|
|
43770
|
+
const subdirPath = join15(locusConfigDir, subdir);
|
|
43771
|
+
if (!existsSync15(subdirPath)) {
|
|
43772
|
+
mkdirSync7(subdirPath, { recursive: true });
|
|
42705
43773
|
result.directoriesCreated.push(`.locus/${subdir}`);
|
|
42706
43774
|
}
|
|
42707
43775
|
}
|
|
42708
43776
|
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42709
|
-
if (!
|
|
42710
|
-
|
|
43777
|
+
if (!existsSync15(learningsMdPath)) {
|
|
43778
|
+
writeFileSync7(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
42711
43779
|
result.directoriesCreated.push(".locus/LEARNINGS.md");
|
|
42712
43780
|
}
|
|
42713
|
-
const gitignorePath =
|
|
42714
|
-
const gitignoreBefore =
|
|
43781
|
+
const gitignorePath = join15(this.projectPath, ".gitignore");
|
|
43782
|
+
const gitignoreBefore = existsSync15(gitignorePath) ? readFileSync13(gitignorePath, "utf-8") : "";
|
|
42715
43783
|
updateGitignore(this.projectPath);
|
|
42716
|
-
const gitignoreAfter =
|
|
43784
|
+
const gitignoreAfter = readFileSync13(gitignorePath, "utf-8");
|
|
42717
43785
|
if (gitignoreBefore !== gitignoreAfter) {
|
|
42718
43786
|
result.gitignoreUpdated = true;
|
|
42719
43787
|
}
|
|
@@ -42723,83 +43791,11 @@ class ConfigManager {
|
|
|
42723
43791
|
saveConfig(config2) {
|
|
42724
43792
|
const { $schema: _2, ...rest } = config2;
|
|
42725
43793
|
const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
|
|
42726
|
-
const
|
|
42727
|
-
|
|
43794
|
+
const path3 = getLocusPath(this.projectPath, "configFile");
|
|
43795
|
+
writeFileSync7(path3, JSON.stringify(ordered, null, 2));
|
|
42728
43796
|
}
|
|
42729
43797
|
}
|
|
42730
43798
|
|
|
42731
|
-
// src/utils/banner.ts
|
|
42732
|
-
init_index_node();
|
|
42733
|
-
|
|
42734
|
-
// src/utils/version.ts
|
|
42735
|
-
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
42736
|
-
import { dirname as dirname3, join as join13 } from "node:path";
|
|
42737
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
42738
|
-
function getVersion() {
|
|
42739
|
-
try {
|
|
42740
|
-
const __filename2 = fileURLToPath3(import.meta.url);
|
|
42741
|
-
const __dirname2 = dirname3(__filename2);
|
|
42742
|
-
const bundledPath = join13(__dirname2, "..", "package.json");
|
|
42743
|
-
const sourcePath = join13(__dirname2, "..", "..", "package.json");
|
|
42744
|
-
if (existsSync12(bundledPath)) {
|
|
42745
|
-
const pkg = JSON.parse(readFileSync11(bundledPath, "utf-8"));
|
|
42746
|
-
if (pkg.name === "@locusai/cli") {
|
|
42747
|
-
return pkg.version || "0.0.0";
|
|
42748
|
-
}
|
|
42749
|
-
}
|
|
42750
|
-
if (existsSync12(sourcePath)) {
|
|
42751
|
-
const pkg = JSON.parse(readFileSync11(sourcePath, "utf-8"));
|
|
42752
|
-
if (pkg.name === "@locusai/cli") {
|
|
42753
|
-
return pkg.version || "0.0.0";
|
|
42754
|
-
}
|
|
42755
|
-
}
|
|
42756
|
-
} catch {}
|
|
42757
|
-
return "0.0.0";
|
|
42758
|
-
}
|
|
42759
|
-
var VERSION2 = getVersion();
|
|
42760
|
-
|
|
42761
|
-
// src/utils/banner.ts
|
|
42762
|
-
function printBanner() {
|
|
42763
|
-
console.log(c.primary(`
|
|
42764
|
-
_ ____ ____ _ _ ____
|
|
42765
|
-
| | / __ \\ / ___| | | |/ ___|
|
|
42766
|
-
| | | | | | | | | | |\\___ \\
|
|
42767
|
-
| |___| |__| | |___| |_| |___) |
|
|
42768
|
-
|_____|\\____/ \\____|\\___/|____/ ${c.dim(`v${VERSION2}`)}
|
|
42769
|
-
`));
|
|
42770
|
-
}
|
|
42771
|
-
// src/utils/helpers.ts
|
|
42772
|
-
init_index_node();
|
|
42773
|
-
import { existsSync as existsSync13 } from "node:fs";
|
|
42774
|
-
import { join as join14 } from "node:path";
|
|
42775
|
-
function isProjectInitialized(projectPath) {
|
|
42776
|
-
const locusDir = join14(projectPath, LOCUS_CONFIG.dir);
|
|
42777
|
-
const configPath = join14(locusDir, LOCUS_CONFIG.configFile);
|
|
42778
|
-
return existsSync13(locusDir) && existsSync13(configPath);
|
|
42779
|
-
}
|
|
42780
|
-
function requireInitialization(projectPath, command) {
|
|
42781
|
-
if (!isProjectInitialized(projectPath)) {
|
|
42782
|
-
console.error(`
|
|
42783
|
-
${c.error("✖ Error")} ${c.red(`Locus is not initialized in this directory.`)}
|
|
42784
|
-
|
|
42785
|
-
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
42786
|
-
|
|
42787
|
-
To initialize Locus in this directory, run:
|
|
42788
|
-
${c.primary("locus init")}
|
|
42789
|
-
|
|
42790
|
-
This will create a ${c.dim(".locus")} directory with the necessary configuration.
|
|
42791
|
-
`);
|
|
42792
|
-
process.exit(1);
|
|
42793
|
-
}
|
|
42794
|
-
}
|
|
42795
|
-
function resolveProvider3(input) {
|
|
42796
|
-
if (!input)
|
|
42797
|
-
return PROVIDER.CLAUDE;
|
|
42798
|
-
if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
|
|
42799
|
-
return input;
|
|
42800
|
-
console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
|
|
42801
|
-
process.exit(1);
|
|
42802
|
-
}
|
|
42803
43799
|
// src/workspace-resolver.ts
|
|
42804
43800
|
init_index_node();
|
|
42805
43801
|
|
|
@@ -42844,7 +43840,7 @@ async function docsCommand(args) {
|
|
|
42844
43840
|
}
|
|
42845
43841
|
}
|
|
42846
43842
|
async function docsSyncCommand(args) {
|
|
42847
|
-
const { values } =
|
|
43843
|
+
const { values } = parseArgs2({
|
|
42848
43844
|
args,
|
|
42849
43845
|
options: {
|
|
42850
43846
|
"api-key": { type: "string" },
|
|
@@ -42961,7 +43957,7 @@ function showDocsSyncHelp() {
|
|
|
42961
43957
|
// src/commands/exec.ts
|
|
42962
43958
|
init_index_node();
|
|
42963
43959
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
42964
|
-
import { parseArgs as
|
|
43960
|
+
import { parseArgs as parseArgs3 } from "node:util";
|
|
42965
43961
|
|
|
42966
43962
|
// src/display/json-stream-renderer.ts
|
|
42967
43963
|
init_src();
|
|
@@ -43254,7 +44250,7 @@ function showSessionsHelp() {
|
|
|
43254
44250
|
|
|
43255
44251
|
// src/commands/exec.ts
|
|
43256
44252
|
async function execCommand(args) {
|
|
43257
|
-
const { values, positionals } =
|
|
44253
|
+
const { values, positionals } = parseArgs3({
|
|
43258
44254
|
args,
|
|
43259
44255
|
options: {
|
|
43260
44256
|
model: { type: "string" },
|
|
@@ -43265,6 +44261,7 @@ async function execCommand(args) {
|
|
|
43265
44261
|
"no-status": { type: "boolean" },
|
|
43266
44262
|
interactive: { type: "boolean", short: "i" },
|
|
43267
44263
|
session: { type: "string", short: "s" },
|
|
44264
|
+
"session-id": { type: "string" },
|
|
43268
44265
|
"json-stream": { type: "boolean" }
|
|
43269
44266
|
},
|
|
43270
44267
|
strict: false
|
|
@@ -43393,7 +44390,7 @@ async function execCommand(args) {
|
|
|
43393
44390
|
}
|
|
43394
44391
|
}
|
|
43395
44392
|
async function execJsonStream(values, positionals, projectPath) {
|
|
43396
|
-
const sessionId = values.session ?? randomUUID2();
|
|
44393
|
+
const sessionId = values["session-id"] ?? values.session ?? randomUUID2();
|
|
43397
44394
|
const execSettings = new SettingsManager(projectPath).load();
|
|
43398
44395
|
const provider = resolveProvider3(values.provider || execSettings.provider);
|
|
43399
44396
|
const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
|
|
@@ -43462,6 +44459,11 @@ function showHelp2() {
|
|
|
43462
44459
|
${c.dim("remove Remove all settings")}
|
|
43463
44460
|
${c.success("index")} Index the codebase for AI context
|
|
43464
44461
|
${c.success("run")} Start agent to work on tasks sequentially
|
|
44462
|
+
${c.success("discuss")} Start an interactive AI discussion on a topic
|
|
44463
|
+
${c.dim("--list List all discussions")}
|
|
44464
|
+
${c.dim("--show <id> Show discussion details")}
|
|
44465
|
+
${c.dim("--archive <id> Archive a discussion")}
|
|
44466
|
+
${c.dim("--delete <id> Delete a discussion")}
|
|
43465
44467
|
${c.success("plan")} Run async planning meeting to create sprint plans
|
|
43466
44468
|
${c.success("docs")} Manage workspace docs
|
|
43467
44469
|
${c.dim("sync Sync docs from API to .locus/documents")}
|
|
@@ -43499,6 +44501,7 @@ function showHelp2() {
|
|
|
43499
44501
|
${c.dim("$")} ${c.primary("locus review")}
|
|
43500
44502
|
${c.dim("$")} ${c.primary("locus review local")}
|
|
43501
44503
|
${c.dim("$")} ${c.primary("locus telegram setup")}
|
|
44504
|
+
${c.dim("$")} ${c.primary('locus discuss "how should we design the auth system?"')}
|
|
43502
44505
|
${c.dim("$")} ${c.primary("locus exec sessions list")}
|
|
43503
44506
|
|
|
43504
44507
|
For more information, visit: ${c.underline("https://docs.locusai.dev")}
|
|
@@ -43506,7 +44509,7 @@ function showHelp2() {
|
|
|
43506
44509
|
}
|
|
43507
44510
|
// src/commands/index-codebase.ts
|
|
43508
44511
|
init_index_node();
|
|
43509
|
-
import { parseArgs as
|
|
44512
|
+
import { parseArgs as parseArgs4 } from "node:util";
|
|
43510
44513
|
|
|
43511
44514
|
// src/tree-summarizer.ts
|
|
43512
44515
|
init_index_node();
|
|
@@ -43534,7 +44537,7 @@ ${tree}`;
|
|
|
43534
44537
|
|
|
43535
44538
|
// src/commands/index-codebase.ts
|
|
43536
44539
|
async function indexCommand(args) {
|
|
43537
|
-
const { values } =
|
|
44540
|
+
const { values } = parseArgs4({
|
|
43538
44541
|
args,
|
|
43539
44542
|
options: {
|
|
43540
44543
|
dir: { type: "string" },
|
|
@@ -43632,9 +44635,9 @@ async function initCommand() {
|
|
|
43632
44635
|
}
|
|
43633
44636
|
// src/commands/plan.ts
|
|
43634
44637
|
init_index_node();
|
|
43635
|
-
import { existsSync as
|
|
43636
|
-
import { join as
|
|
43637
|
-
import { parseArgs as
|
|
44638
|
+
import { existsSync as existsSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
44639
|
+
import { join as join16 } from "node:path";
|
|
44640
|
+
import { parseArgs as parseArgs5 } from "node:util";
|
|
43638
44641
|
function normalizePlanIdArgs(args) {
|
|
43639
44642
|
const planIdFlags = new Set(["--approve", "--reject", "--cancel", "--show"]);
|
|
43640
44643
|
const result = [];
|
|
@@ -43653,7 +44656,7 @@ function normalizePlanIdArgs(args) {
|
|
|
43653
44656
|
}
|
|
43654
44657
|
async function planCommand(args) {
|
|
43655
44658
|
const normalizedArgs = normalizePlanIdArgs(args);
|
|
43656
|
-
const { values, positionals } =
|
|
44659
|
+
const { values, positionals } = parseArgs5({
|
|
43657
44660
|
args: normalizedArgs,
|
|
43658
44661
|
options: {
|
|
43659
44662
|
approve: { type: "string" },
|
|
@@ -43740,9 +44743,9 @@ async function planCommand(args) {
|
|
|
43740
44743
|
try {
|
|
43741
44744
|
const result = await meeting.run(directive, feedback);
|
|
43742
44745
|
planManager.save(result.plan);
|
|
43743
|
-
const tempFile =
|
|
43744
|
-
if (
|
|
43745
|
-
|
|
44746
|
+
const tempFile = join16(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
|
|
44747
|
+
if (existsSync16(tempFile)) {
|
|
44748
|
+
unlinkSync5(tempFile);
|
|
43746
44749
|
}
|
|
43747
44750
|
console.log(`
|
|
43748
44751
|
${c.success("✔")} ${c.success("Planning meeting complete!")}
|
|
@@ -43938,9 +44941,9 @@ function showPlanHelp() {
|
|
|
43938
44941
|
}
|
|
43939
44942
|
// src/commands/review.ts
|
|
43940
44943
|
init_index_node();
|
|
43941
|
-
import { existsSync as
|
|
43942
|
-
import { join as
|
|
43943
|
-
import { parseArgs as
|
|
44944
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
44945
|
+
import { join as join17 } from "node:path";
|
|
44946
|
+
import { parseArgs as parseArgs6 } from "node:util";
|
|
43944
44947
|
async function reviewCommand(args) {
|
|
43945
44948
|
const subcommand = args[0];
|
|
43946
44949
|
if (subcommand === "local") {
|
|
@@ -43949,7 +44952,7 @@ async function reviewCommand(args) {
|
|
|
43949
44952
|
return reviewPrsCommand(args);
|
|
43950
44953
|
}
|
|
43951
44954
|
async function reviewPrsCommand(args) {
|
|
43952
|
-
const { values } =
|
|
44955
|
+
const { values } = parseArgs6({
|
|
43953
44956
|
args,
|
|
43954
44957
|
options: {
|
|
43955
44958
|
"api-key": { type: "string" },
|
|
@@ -44035,7 +45038,7 @@ ${c.info("Received shutdown signal. Stopping reviewer...")}`);
|
|
|
44035
45038
|
await reviewer.run();
|
|
44036
45039
|
}
|
|
44037
45040
|
async function reviewLocalCommand(args) {
|
|
44038
|
-
const { values } =
|
|
45041
|
+
const { values } = parseArgs6({
|
|
44039
45042
|
args,
|
|
44040
45043
|
options: {
|
|
44041
45044
|
model: { type: "string" },
|
|
@@ -44078,13 +45081,13 @@ async function reviewLocalCommand(args) {
|
|
|
44078
45081
|
`);
|
|
44079
45082
|
return;
|
|
44080
45083
|
}
|
|
44081
|
-
const reviewsDir =
|
|
44082
|
-
if (!
|
|
44083
|
-
|
|
45084
|
+
const reviewsDir = join17(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
|
|
45085
|
+
if (!existsSync17(reviewsDir)) {
|
|
45086
|
+
mkdirSync8(reviewsDir, { recursive: true });
|
|
44084
45087
|
}
|
|
44085
45088
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
44086
|
-
const reportPath =
|
|
44087
|
-
|
|
45089
|
+
const reportPath = join17(reviewsDir, `review-${timestamp}.md`);
|
|
45090
|
+
writeFileSync8(reportPath, report, "utf-8");
|
|
44088
45091
|
console.log(`
|
|
44089
45092
|
${c.success("✔")} ${c.success("Review complete!")}`);
|
|
44090
45093
|
console.log(` ${c.dim("Report saved to:")} ${c.primary(reportPath)}
|
|
@@ -44092,9 +45095,9 @@ async function reviewLocalCommand(args) {
|
|
|
44092
45095
|
}
|
|
44093
45096
|
// src/commands/run.ts
|
|
44094
45097
|
init_index_node();
|
|
44095
|
-
import { parseArgs as
|
|
45098
|
+
import { parseArgs as parseArgs7 } from "node:util";
|
|
44096
45099
|
async function runCommand(args) {
|
|
44097
|
-
const { values } =
|
|
45100
|
+
const { values } = parseArgs7({
|
|
44098
45101
|
args,
|
|
44099
45102
|
options: {
|
|
44100
45103
|
"api-key": { type: "string" },
|
|
@@ -44176,11 +45179,11 @@ ${c.info(`Received ${signal}. Stopping agent and cleaning up...`)}`);
|
|
|
44176
45179
|
// src/commands/telegram.ts
|
|
44177
45180
|
init_index_node();
|
|
44178
45181
|
import { spawn as spawn4 } from "node:child_process";
|
|
44179
|
-
import { existsSync as
|
|
44180
|
-
import { join as
|
|
44181
|
-
import { createInterface as
|
|
45182
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
45183
|
+
import { join as join18 } from "node:path";
|
|
45184
|
+
import { createInterface as createInterface4 } from "node:readline";
|
|
44182
45185
|
function ask2(question) {
|
|
44183
|
-
const rl =
|
|
45186
|
+
const rl = createInterface4({
|
|
44184
45187
|
input: process.stdin,
|
|
44185
45188
|
output: process.stdout
|
|
44186
45189
|
});
|
|
@@ -44411,8 +45414,8 @@ function runBotCommand(projectPath) {
|
|
|
44411
45414
|
`);
|
|
44412
45415
|
process.exit(1);
|
|
44413
45416
|
}
|
|
44414
|
-
const monorepoTelegramEntry =
|
|
44415
|
-
const isMonorepo =
|
|
45417
|
+
const monorepoTelegramEntry = join18(projectPath, "packages/telegram/src/index.ts");
|
|
45418
|
+
const isMonorepo = existsSync18(monorepoTelegramEntry);
|
|
44416
45419
|
let cmd;
|
|
44417
45420
|
let args;
|
|
44418
45421
|
if (isMonorepo) {
|
|
@@ -44571,6 +45574,10 @@ var isJsonStream = process.argv.includes("--json-stream");
|
|
|
44571
45574
|
async function main() {
|
|
44572
45575
|
const command = process.argv[2];
|
|
44573
45576
|
const args = process.argv.slice(3);
|
|
45577
|
+
if (command === "--json-stream") {
|
|
45578
|
+
await execCommand([command, ...args]);
|
|
45579
|
+
return;
|
|
45580
|
+
}
|
|
44574
45581
|
if (command !== "exec" && !isJsonStream) {
|
|
44575
45582
|
printBanner();
|
|
44576
45583
|
}
|
|
@@ -44587,6 +45594,9 @@ async function main() {
|
|
|
44587
45594
|
case "exec":
|
|
44588
45595
|
await execCommand(args);
|
|
44589
45596
|
break;
|
|
45597
|
+
case "discuss":
|
|
45598
|
+
await discussCommand(args);
|
|
45599
|
+
break;
|
|
44590
45600
|
case "plan":
|
|
44591
45601
|
await planCommand(args);
|
|
44592
45602
|
break;
|