@locusai/commands 0.16.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/dist/api-context.d.ts +31 -0
- package/dist/api-context.d.ts.map +1 -0
- package/dist/api-context.js +39 -0
- package/dist/artifacts.d.ts +42 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +100 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +30 -0
- package/dist/discussions.d.ts +26 -0
- package/dist/discussions.d.ts.map +1 -0
- package/dist/discussions.js +32 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +331 -0
- package/dist/logger.d.ts +15 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +26 -0
- package/dist/plans.d.ts +28 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +34 -0
- package/dist/settings.d.ts +25 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +42 -0
- package/dist/workspace.d.ts +19 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +48 -0
- package/package.json +26 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LocusClient } from "@locusai/sdk";
|
|
2
|
+
import type { AiProvider } from "@locusai/sdk/node";
|
|
3
|
+
export interface ApiContextOptions {
|
|
4
|
+
projectPath: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
workspaceId?: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ApiContext {
|
|
12
|
+
client: LocusClient;
|
|
13
|
+
workspaceId: string;
|
|
14
|
+
apiKey: string;
|
|
15
|
+
apiBase: string;
|
|
16
|
+
}
|
|
17
|
+
export interface AiSettings {
|
|
18
|
+
provider: AiProvider;
|
|
19
|
+
model: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve API context (client + workspace) from options and settings.
|
|
23
|
+
* Merges explicit options with persisted settings. Throws on missing API key.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveApiContext(options: ApiContextOptions): Promise<ApiContext>;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve AI provider and model from options and settings.
|
|
28
|
+
* Merges explicit options with persisted settings, applying defaults.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveAiSettings(options: ApiContextOptions): AiSettings;
|
|
31
|
+
//# sourceMappingURL=api-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-context.d.ts","sourceRoot":"","sources":["../src/api-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQpD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,UAAU,CAAC,CA0BrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAOxE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LocusClient } from "@locusai/sdk";
|
|
2
|
+
import { DEFAULT_MODEL } from "@locusai/sdk/node";
|
|
3
|
+
import { resolveProvider } from "./config.js";
|
|
4
|
+
import { SettingsManager } from "./settings.js";
|
|
5
|
+
import { WorkspaceResolver } from "./workspace.js";
|
|
6
|
+
const DEFAULT_API_BASE = "https://api.locusai.dev/api";
|
|
7
|
+
/**
|
|
8
|
+
* Resolve API context (client + workspace) from options and settings.
|
|
9
|
+
* Merges explicit options with persisted settings. Throws on missing API key.
|
|
10
|
+
*/
|
|
11
|
+
export async function resolveApiContext(options) {
|
|
12
|
+
const settings = new SettingsManager(options.projectPath).load();
|
|
13
|
+
const apiKey = options.apiKey || settings.apiKey;
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
throw new Error("API key is required. Configure with: locus config setup --api-key <key>");
|
|
16
|
+
}
|
|
17
|
+
const apiBase = options.apiUrl || settings.apiUrl || DEFAULT_API_BASE;
|
|
18
|
+
const resolver = new WorkspaceResolver({
|
|
19
|
+
apiKey,
|
|
20
|
+
apiBase,
|
|
21
|
+
workspaceId: options.workspaceId,
|
|
22
|
+
});
|
|
23
|
+
const workspaceId = await resolver.resolve();
|
|
24
|
+
const client = new LocusClient({
|
|
25
|
+
baseUrl: apiBase,
|
|
26
|
+
token: apiKey,
|
|
27
|
+
});
|
|
28
|
+
return { client, workspaceId, apiKey, apiBase };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve AI provider and model from options and settings.
|
|
32
|
+
* Merges explicit options with persisted settings, applying defaults.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveAiSettings(options) {
|
|
35
|
+
const settings = new SettingsManager(options.projectPath).load();
|
|
36
|
+
const provider = resolveProvider(options.provider || settings.provider);
|
|
37
|
+
const model = options.model || settings.model || DEFAULT_MODEL[provider];
|
|
38
|
+
return { provider, model };
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface ArtifactInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
fileName: string;
|
|
4
|
+
createdAt: Date;
|
|
5
|
+
size: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* List artifacts sorted by creation time (newest first).
|
|
9
|
+
*/
|
|
10
|
+
export declare function listArtifacts(projectPath: string): ArtifactInfo[];
|
|
11
|
+
/**
|
|
12
|
+
* Read an artifact's content by name (with or without .md extension).
|
|
13
|
+
*/
|
|
14
|
+
export declare function readArtifact(projectPath: string, name: string): {
|
|
15
|
+
content: string;
|
|
16
|
+
info: ArtifactInfo;
|
|
17
|
+
} | null;
|
|
18
|
+
/**
|
|
19
|
+
* Find an artifact by exact or partial name match.
|
|
20
|
+
*
|
|
21
|
+
* Returns:
|
|
22
|
+
* - `{ match, content, info }` if exactly one artifact matches
|
|
23
|
+
* - `{ ambiguous: ArtifactInfo[] }` if multiple artifacts match
|
|
24
|
+
* - `null` if no artifacts match
|
|
25
|
+
*/
|
|
26
|
+
export declare function findArtifact(projectPath: string, name: string): {
|
|
27
|
+
match: true;
|
|
28
|
+
content: string;
|
|
29
|
+
info: ArtifactInfo;
|
|
30
|
+
} | {
|
|
31
|
+
match: false;
|
|
32
|
+
ambiguous: ArtifactInfo[];
|
|
33
|
+
} | null;
|
|
34
|
+
/**
|
|
35
|
+
* Format file size for display.
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatSize(bytes: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Format a date for display.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatDate(date: Date): string;
|
|
42
|
+
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAuBjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,CAqBhD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAEV;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GACpD;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,GAC3C,IAAI,CAyBP;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAQ7C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getLocusPath } from "@locusai/sdk/node";
|
|
4
|
+
/**
|
|
5
|
+
* List artifacts sorted by creation time (newest first).
|
|
6
|
+
*/
|
|
7
|
+
export function listArtifacts(projectPath) {
|
|
8
|
+
const artifactsDir = getLocusPath(projectPath, "artifactsDir");
|
|
9
|
+
if (!existsSync(artifactsDir)) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const files = readdirSync(artifactsDir).filter((f) => f.endsWith(".md"));
|
|
13
|
+
return files
|
|
14
|
+
.map((fileName) => {
|
|
15
|
+
const filePath = join(artifactsDir, fileName);
|
|
16
|
+
const stat = statSync(filePath);
|
|
17
|
+
const name = fileName.replace(/\.md$/, "");
|
|
18
|
+
return {
|
|
19
|
+
name,
|
|
20
|
+
fileName,
|
|
21
|
+
createdAt: stat.birthtime,
|
|
22
|
+
size: stat.size,
|
|
23
|
+
};
|
|
24
|
+
})
|
|
25
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read an artifact's content by name (with or without .md extension).
|
|
29
|
+
*/
|
|
30
|
+
export function readArtifact(projectPath, name) {
|
|
31
|
+
const artifactsDir = getLocusPath(projectPath, "artifactsDir");
|
|
32
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
33
|
+
const filePath = join(artifactsDir, fileName);
|
|
34
|
+
if (!existsSync(filePath)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const stat = statSync(filePath);
|
|
38
|
+
const content = readFileSync(filePath, "utf-8");
|
|
39
|
+
return {
|
|
40
|
+
content,
|
|
41
|
+
info: {
|
|
42
|
+
name: fileName.replace(/\.md$/, ""),
|
|
43
|
+
fileName,
|
|
44
|
+
createdAt: stat.birthtime,
|
|
45
|
+
size: stat.size,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Find an artifact by exact or partial name match.
|
|
51
|
+
*
|
|
52
|
+
* Returns:
|
|
53
|
+
* - `{ match, content, info }` if exactly one artifact matches
|
|
54
|
+
* - `{ ambiguous: ArtifactInfo[] }` if multiple artifacts match
|
|
55
|
+
* - `null` if no artifacts match
|
|
56
|
+
*/
|
|
57
|
+
export function findArtifact(projectPath, name) {
|
|
58
|
+
// Try exact match first
|
|
59
|
+
const exact = readArtifact(projectPath, name);
|
|
60
|
+
if (exact) {
|
|
61
|
+
return { match: true, content: exact.content, info: exact.info };
|
|
62
|
+
}
|
|
63
|
+
// Try partial match
|
|
64
|
+
const artifacts = listArtifacts(projectPath);
|
|
65
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
66
|
+
if (matches.length === 1) {
|
|
67
|
+
const result = readArtifact(projectPath, matches[0].name);
|
|
68
|
+
if (result) {
|
|
69
|
+
return { match: true, content: result.content, info: result.info };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (matches.length > 1) {
|
|
73
|
+
return { match: false, ambiguous: matches };
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Format file size for display.
|
|
79
|
+
*/
|
|
80
|
+
export function formatSize(bytes) {
|
|
81
|
+
if (bytes < 1024)
|
|
82
|
+
return `${bytes}B`;
|
|
83
|
+
const kb = bytes / 1024;
|
|
84
|
+
if (kb < 1024)
|
|
85
|
+
return `${kb.toFixed(1)}KB`;
|
|
86
|
+
const mb = kb / 1024;
|
|
87
|
+
return `${mb.toFixed(1)}MB`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Format a date for display.
|
|
91
|
+
*/
|
|
92
|
+
export function formatDate(date) {
|
|
93
|
+
return date.toLocaleDateString("en-US", {
|
|
94
|
+
year: "numeric",
|
|
95
|
+
month: "short",
|
|
96
|
+
day: "numeric",
|
|
97
|
+
hour: "2-digit",
|
|
98
|
+
minute: "2-digit",
|
|
99
|
+
});
|
|
100
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AiProvider } from "@locusai/sdk/node";
|
|
2
|
+
/**
|
|
3
|
+
* Check if a project has been initialized with Locus.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isProjectInitialized(projectPath: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve and validate AI provider from input string.
|
|
8
|
+
* Returns the provider or throws an error for invalid input.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveProvider(input?: string): AiProvider;
|
|
11
|
+
/**
|
|
12
|
+
* Mask a secret value for display (show first 4 + last 4 chars).
|
|
13
|
+
*/
|
|
14
|
+
export declare function maskSecret(value: string): string;
|
|
15
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAA0B,MAAM,mBAAmB,CAAC;AAE5E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAIjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,CAI1D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { LOCUS_CONFIG, PROVIDER } from "@locusai/sdk/node";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a project has been initialized with Locus.
|
|
6
|
+
*/
|
|
7
|
+
export function isProjectInitialized(projectPath) {
|
|
8
|
+
const locusDir = join(projectPath, LOCUS_CONFIG.dir);
|
|
9
|
+
const configPath = join(locusDir, LOCUS_CONFIG.configFile);
|
|
10
|
+
return existsSync(locusDir) && existsSync(configPath);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve and validate AI provider from input string.
|
|
14
|
+
* Returns the provider or throws an error for invalid input.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveProvider(input) {
|
|
17
|
+
if (!input)
|
|
18
|
+
return PROVIDER.CLAUDE;
|
|
19
|
+
if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
|
|
20
|
+
return input;
|
|
21
|
+
throw new Error(`Invalid provider '${input}'. Must be 'claude' or 'codex'.`);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mask a secret value for display (show first 4 + last 4 chars).
|
|
25
|
+
*/
|
|
26
|
+
export function maskSecret(value) {
|
|
27
|
+
if (value.length <= 8)
|
|
28
|
+
return "****";
|
|
29
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DiscussionManager } from "@locusai/sdk/node";
|
|
2
|
+
export interface DiscussionSummary {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
status: string;
|
|
6
|
+
messageCount: number;
|
|
7
|
+
insightCount: number;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* List all discussions with summary info.
|
|
12
|
+
*/
|
|
13
|
+
export declare function listDiscussions(manager: DiscussionManager): DiscussionSummary[];
|
|
14
|
+
/**
|
|
15
|
+
* Get discussion markdown content. Returns null if not found.
|
|
16
|
+
*/
|
|
17
|
+
export declare function showDiscussion(manager: DiscussionManager, id: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Archive a discussion by ID. Throws if not found.
|
|
20
|
+
*/
|
|
21
|
+
export declare function archiveDiscussion(manager: DiscussionManager, id: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Delete a discussion by ID. Throws if not found.
|
|
24
|
+
*/
|
|
25
|
+
export declare function deleteDiscussion(manager: DiscussionManager, id: string): void;
|
|
26
|
+
//# sourceMappingURL=discussions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discussions.d.ts","sourceRoot":"","sources":["../src/discussions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,GACzB,iBAAiB,EAAE,CAUrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,EAAE,EAAE,MAAM,GACT,MAAM,GAAG,IAAI,CAEf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,EAAE,EAAE,MAAM,GACT,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,EAAE,EAAE,MAAM,GACT,IAAI,CAEN"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List all discussions with summary info.
|
|
3
|
+
*/
|
|
4
|
+
export function listDiscussions(manager) {
|
|
5
|
+
const discussions = manager.list();
|
|
6
|
+
return discussions.map((d) => ({
|
|
7
|
+
id: d.id,
|
|
8
|
+
title: d.title,
|
|
9
|
+
status: d.status,
|
|
10
|
+
messageCount: d.messages.length,
|
|
11
|
+
insightCount: d.insights.length,
|
|
12
|
+
createdAt: d.createdAt,
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get discussion markdown content. Returns null if not found.
|
|
17
|
+
*/
|
|
18
|
+
export function showDiscussion(manager, id) {
|
|
19
|
+
return manager.getMarkdown(id);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Archive a discussion by ID. Throws if not found.
|
|
23
|
+
*/
|
|
24
|
+
export function archiveDiscussion(manager, id) {
|
|
25
|
+
manager.archive(id);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Delete a discussion by ID. Throws if not found.
|
|
29
|
+
*/
|
|
30
|
+
export function deleteDiscussion(manager, id) {
|
|
31
|
+
manager.delete(id);
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type ApiContext, type ApiContextOptions, type AiSettings, resolveApiContext, resolveAiSettings, } from "./api-context.js";
|
|
2
|
+
export { type ArtifactInfo, findArtifact, formatDate, formatSize, listArtifacts, readArtifact, } from "./artifacts.js";
|
|
3
|
+
export { isProjectInitialized, maskSecret, resolveProvider, } from "./config.js";
|
|
4
|
+
export { type DiscussionSummary, listDiscussions, showDiscussion, archiveDiscussion, deleteDiscussion, } from "./discussions.js";
|
|
5
|
+
export { createCliLogger, createWorkerLogger, type LogFn, noopLogger, } from "./logger.js";
|
|
6
|
+
export { type PlanSummary, listPlans, showPlan, cancelPlan, rejectPlan, } from "./plans.js";
|
|
7
|
+
export { type LocusSettings, SettingsManager, type TelegramSettings, } from "./settings.js";
|
|
8
|
+
export { resolveWorkspaceId, WorkspaceResolver, type WorkspaceResolverOptions, } from "./workspace.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,KAAK,YAAY,EACjB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,iBAAiB,EACtB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,KAAK,KAAK,EACV,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,WAAW,EAChB,SAAS,EACT,QAAQ,EACR,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,KAAK,aAAa,EAClB,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
+
var __toCommonJS = (from) => {
|
|
7
|
+
var entry = __moduleCache.get(from), desc;
|
|
8
|
+
if (entry)
|
|
9
|
+
return entry;
|
|
10
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
}));
|
|
16
|
+
__moduleCache.set(from, entry);
|
|
17
|
+
return entry;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var exports_src = {};
|
|
31
|
+
__export(exports_src, {
|
|
32
|
+
showPlan: () => showPlan,
|
|
33
|
+
showDiscussion: () => showDiscussion,
|
|
34
|
+
resolveWorkspaceId: () => resolveWorkspaceId,
|
|
35
|
+
resolveProvider: () => resolveProvider,
|
|
36
|
+
resolveApiContext: () => resolveApiContext,
|
|
37
|
+
resolveAiSettings: () => resolveAiSettings,
|
|
38
|
+
rejectPlan: () => rejectPlan,
|
|
39
|
+
readArtifact: () => readArtifact,
|
|
40
|
+
noopLogger: () => import_node4.noopLogger,
|
|
41
|
+
maskSecret: () => maskSecret,
|
|
42
|
+
listPlans: () => listPlans,
|
|
43
|
+
listDiscussions: () => listDiscussions,
|
|
44
|
+
listArtifacts: () => listArtifacts,
|
|
45
|
+
isProjectInitialized: () => isProjectInitialized,
|
|
46
|
+
formatSize: () => formatSize,
|
|
47
|
+
formatDate: () => formatDate,
|
|
48
|
+
findArtifact: () => findArtifact,
|
|
49
|
+
deleteDiscussion: () => deleteDiscussion,
|
|
50
|
+
createWorkerLogger: () => import_node4.createWorkerLogger,
|
|
51
|
+
createCliLogger: () => createCliLogger,
|
|
52
|
+
cancelPlan: () => cancelPlan,
|
|
53
|
+
archiveDiscussion: () => archiveDiscussion,
|
|
54
|
+
WorkspaceResolver: () => WorkspaceResolver,
|
|
55
|
+
SettingsManager: () => SettingsManager
|
|
56
|
+
});
|
|
57
|
+
module.exports = __toCommonJS(exports_src);
|
|
58
|
+
|
|
59
|
+
// src/api-context.ts
|
|
60
|
+
var import_sdk2 = require("@locusai/sdk");
|
|
61
|
+
var import_node5 = require("@locusai/sdk/node");
|
|
62
|
+
|
|
63
|
+
// src/config.ts
|
|
64
|
+
var import_node_fs = require("node:fs");
|
|
65
|
+
var import_node_path = require("node:path");
|
|
66
|
+
var import_node = require("@locusai/sdk/node");
|
|
67
|
+
function isProjectInitialized(projectPath) {
|
|
68
|
+
const locusDir = import_node_path.join(projectPath, import_node.LOCUS_CONFIG.dir);
|
|
69
|
+
const configPath = import_node_path.join(locusDir, import_node.LOCUS_CONFIG.configFile);
|
|
70
|
+
return import_node_fs.existsSync(locusDir) && import_node_fs.existsSync(configPath);
|
|
71
|
+
}
|
|
72
|
+
function resolveProvider(input) {
|
|
73
|
+
if (!input)
|
|
74
|
+
return import_node.PROVIDER.CLAUDE;
|
|
75
|
+
if (input === import_node.PROVIDER.CLAUDE || input === import_node.PROVIDER.CODEX)
|
|
76
|
+
return input;
|
|
77
|
+
throw new Error(`Invalid provider '${input}'. Must be 'claude' or 'codex'.`);
|
|
78
|
+
}
|
|
79
|
+
function maskSecret(value) {
|
|
80
|
+
if (value.length <= 8)
|
|
81
|
+
return "****";
|
|
82
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/settings.ts
|
|
86
|
+
var import_node_fs2 = require("node:fs");
|
|
87
|
+
var import_node_path2 = require("node:path");
|
|
88
|
+
var import_node2 = require("@locusai/sdk/node");
|
|
89
|
+
function getSettingsPath(projectPath) {
|
|
90
|
+
return import_node_path2.join(projectPath, import_node2.LOCUS_CONFIG.dir, import_node2.LOCUS_CONFIG.settingsFile);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
class SettingsManager {
|
|
94
|
+
projectPath;
|
|
95
|
+
constructor(projectPath) {
|
|
96
|
+
this.projectPath = projectPath;
|
|
97
|
+
}
|
|
98
|
+
load() {
|
|
99
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
100
|
+
if (!import_node_fs2.existsSync(settingsPath)) {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
return JSON.parse(import_node_fs2.readFileSync(settingsPath, "utf-8"));
|
|
104
|
+
}
|
|
105
|
+
save(settings) {
|
|
106
|
+
const { $schema: _, ...rest } = settings;
|
|
107
|
+
const ordered = { $schema: import_node2.LOCUS_SCHEMAS.settings, ...rest };
|
|
108
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
109
|
+
import_node_fs2.writeFileSync(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
110
|
+
}
|
|
111
|
+
get(key) {
|
|
112
|
+
return this.load()[key];
|
|
113
|
+
}
|
|
114
|
+
set(key, value) {
|
|
115
|
+
const settings = this.load();
|
|
116
|
+
settings[key] = value;
|
|
117
|
+
this.save(settings);
|
|
118
|
+
}
|
|
119
|
+
remove() {
|
|
120
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
121
|
+
if (import_node_fs2.existsSync(settingsPath)) {
|
|
122
|
+
import_node_fs2.unlinkSync(settingsPath);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exists() {
|
|
126
|
+
return import_node_fs2.existsSync(getSettingsPath(this.projectPath));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/workspace.ts
|
|
131
|
+
var import_sdk = require("@locusai/sdk");
|
|
132
|
+
|
|
133
|
+
// src/logger.ts
|
|
134
|
+
var import_node3 = require("@locusai/sdk/node");
|
|
135
|
+
var import_node4 = require("@locusai/sdk/node");
|
|
136
|
+
function createCliLogger() {
|
|
137
|
+
return (message, level) => {
|
|
138
|
+
const icon = level === "success" ? import_node3.c.success("✔") : level === "error" ? import_node3.c.error("✖") : level === "warn" ? import_node3.c.warning("!") : import_node3.c.info("●");
|
|
139
|
+
console.log(` ${icon} ${message}`);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/workspace.ts
|
|
144
|
+
class WorkspaceResolver {
|
|
145
|
+
options;
|
|
146
|
+
constructor(options) {
|
|
147
|
+
this.options = options;
|
|
148
|
+
}
|
|
149
|
+
async resolve() {
|
|
150
|
+
if (this.options.workspaceId) {
|
|
151
|
+
return this.options.workspaceId;
|
|
152
|
+
}
|
|
153
|
+
const log = this.options.log ?? import_node4.noopLogger;
|
|
154
|
+
try {
|
|
155
|
+
log("Resolving workspace from API key...");
|
|
156
|
+
const client = new import_sdk.LocusClient({
|
|
157
|
+
baseUrl: this.options.apiBase,
|
|
158
|
+
token: this.options.apiKey
|
|
159
|
+
});
|
|
160
|
+
const info = await client.auth.getApiKeyInfo();
|
|
161
|
+
if (info.workspaceId) {
|
|
162
|
+
log(`Resolved workspace: ${info.workspaceId}`);
|
|
163
|
+
return info.workspaceId;
|
|
164
|
+
}
|
|
165
|
+
throw new Error("API key is not associated with a workspace. Please specify a workspace ID.");
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof Error && error.message.includes("API key is not")) {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
throw new Error(`Error resolving workspace: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function resolveWorkspaceId(client, workspaceId) {
|
|
175
|
+
if (workspaceId) {
|
|
176
|
+
return workspaceId;
|
|
177
|
+
}
|
|
178
|
+
const info = await client.auth.getApiKeyInfo();
|
|
179
|
+
if (info.workspaceId) {
|
|
180
|
+
return info.workspaceId;
|
|
181
|
+
}
|
|
182
|
+
throw new Error("Could not resolve workspace from API key. Please set workspaceId in settings.");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/api-context.ts
|
|
186
|
+
var DEFAULT_API_BASE = "https://api.locusai.dev/api";
|
|
187
|
+
async function resolveApiContext(options) {
|
|
188
|
+
const settings = new SettingsManager(options.projectPath).load();
|
|
189
|
+
const apiKey = options.apiKey || settings.apiKey;
|
|
190
|
+
if (!apiKey) {
|
|
191
|
+
throw new Error("API key is required. Configure with: locus config setup --api-key <key>");
|
|
192
|
+
}
|
|
193
|
+
const apiBase = options.apiUrl || settings.apiUrl || DEFAULT_API_BASE;
|
|
194
|
+
const resolver = new WorkspaceResolver({
|
|
195
|
+
apiKey,
|
|
196
|
+
apiBase,
|
|
197
|
+
workspaceId: options.workspaceId
|
|
198
|
+
});
|
|
199
|
+
const workspaceId = await resolver.resolve();
|
|
200
|
+
const client = new import_sdk2.LocusClient({
|
|
201
|
+
baseUrl: apiBase,
|
|
202
|
+
token: apiKey
|
|
203
|
+
});
|
|
204
|
+
return { client, workspaceId, apiKey, apiBase };
|
|
205
|
+
}
|
|
206
|
+
function resolveAiSettings(options) {
|
|
207
|
+
const settings = new SettingsManager(options.projectPath).load();
|
|
208
|
+
const provider = resolveProvider(options.provider || settings.provider);
|
|
209
|
+
const model = options.model || settings.model || import_node5.DEFAULT_MODEL[provider];
|
|
210
|
+
return { provider, model };
|
|
211
|
+
}
|
|
212
|
+
// src/artifacts.ts
|
|
213
|
+
var import_node_fs3 = require("node:fs");
|
|
214
|
+
var import_node_path3 = require("node:path");
|
|
215
|
+
var import_node6 = require("@locusai/sdk/node");
|
|
216
|
+
function listArtifacts(projectPath) {
|
|
217
|
+
const artifactsDir = import_node6.getLocusPath(projectPath, "artifactsDir");
|
|
218
|
+
if (!import_node_fs3.existsSync(artifactsDir)) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const files = import_node_fs3.readdirSync(artifactsDir).filter((f) => f.endsWith(".md"));
|
|
222
|
+
return files.map((fileName) => {
|
|
223
|
+
const filePath = import_node_path3.join(artifactsDir, fileName);
|
|
224
|
+
const stat = import_node_fs3.statSync(filePath);
|
|
225
|
+
const name = fileName.replace(/\.md$/, "");
|
|
226
|
+
return {
|
|
227
|
+
name,
|
|
228
|
+
fileName,
|
|
229
|
+
createdAt: stat.birthtime,
|
|
230
|
+
size: stat.size
|
|
231
|
+
};
|
|
232
|
+
}).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
233
|
+
}
|
|
234
|
+
function readArtifact(projectPath, name) {
|
|
235
|
+
const artifactsDir = import_node6.getLocusPath(projectPath, "artifactsDir");
|
|
236
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
237
|
+
const filePath = import_node_path3.join(artifactsDir, fileName);
|
|
238
|
+
if (!import_node_fs3.existsSync(filePath)) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const stat = import_node_fs3.statSync(filePath);
|
|
242
|
+
const content = import_node_fs3.readFileSync(filePath, "utf-8");
|
|
243
|
+
return {
|
|
244
|
+
content,
|
|
245
|
+
info: {
|
|
246
|
+
name: fileName.replace(/\.md$/, ""),
|
|
247
|
+
fileName,
|
|
248
|
+
createdAt: stat.birthtime,
|
|
249
|
+
size: stat.size
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function findArtifact(projectPath, name) {
|
|
254
|
+
const exact = readArtifact(projectPath, name);
|
|
255
|
+
if (exact) {
|
|
256
|
+
return { match: true, content: exact.content, info: exact.info };
|
|
257
|
+
}
|
|
258
|
+
const artifacts = listArtifacts(projectPath);
|
|
259
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
260
|
+
if (matches.length === 1) {
|
|
261
|
+
const result = readArtifact(projectPath, matches[0].name);
|
|
262
|
+
if (result) {
|
|
263
|
+
return { match: true, content: result.content, info: result.info };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (matches.length > 1) {
|
|
267
|
+
return { match: false, ambiguous: matches };
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
function formatSize(bytes) {
|
|
272
|
+
if (bytes < 1024)
|
|
273
|
+
return `${bytes}B`;
|
|
274
|
+
const kb = bytes / 1024;
|
|
275
|
+
if (kb < 1024)
|
|
276
|
+
return `${kb.toFixed(1)}KB`;
|
|
277
|
+
const mb = kb / 1024;
|
|
278
|
+
return `${mb.toFixed(1)}MB`;
|
|
279
|
+
}
|
|
280
|
+
function formatDate(date) {
|
|
281
|
+
return date.toLocaleDateString("en-US", {
|
|
282
|
+
year: "numeric",
|
|
283
|
+
month: "short",
|
|
284
|
+
day: "numeric",
|
|
285
|
+
hour: "2-digit",
|
|
286
|
+
minute: "2-digit"
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// src/discussions.ts
|
|
290
|
+
function listDiscussions(manager) {
|
|
291
|
+
const discussions = manager.list();
|
|
292
|
+
return discussions.map((d) => ({
|
|
293
|
+
id: d.id,
|
|
294
|
+
title: d.title,
|
|
295
|
+
status: d.status,
|
|
296
|
+
messageCount: d.messages.length,
|
|
297
|
+
insightCount: d.insights.length,
|
|
298
|
+
createdAt: d.createdAt
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
function showDiscussion(manager, id) {
|
|
302
|
+
return manager.getMarkdown(id);
|
|
303
|
+
}
|
|
304
|
+
function archiveDiscussion(manager, id) {
|
|
305
|
+
manager.archive(id);
|
|
306
|
+
}
|
|
307
|
+
function deleteDiscussion(manager, id) {
|
|
308
|
+
manager.delete(id);
|
|
309
|
+
}
|
|
310
|
+
// src/plans.ts
|
|
311
|
+
function listPlans(manager, status) {
|
|
312
|
+
const plans = manager.list(status);
|
|
313
|
+
return plans.map((p) => ({
|
|
314
|
+
id: p.id,
|
|
315
|
+
name: p.name,
|
|
316
|
+
status: p.status,
|
|
317
|
+
taskCount: p.tasks.length,
|
|
318
|
+
directive: p.directive,
|
|
319
|
+
createdAt: p.createdAt,
|
|
320
|
+
feedback: p.feedback
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
function showPlan(manager, idOrSlug) {
|
|
324
|
+
return manager.getMarkdown(idOrSlug);
|
|
325
|
+
}
|
|
326
|
+
function cancelPlan(manager, idOrSlug) {
|
|
327
|
+
manager.cancel(idOrSlug);
|
|
328
|
+
}
|
|
329
|
+
function rejectPlan(manager, idOrSlug, feedback) {
|
|
330
|
+
return manager.reject(idOrSlug, feedback);
|
|
331
|
+
}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized logging utilities for Locus packages.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports core logger types from SDK and adds CLI-specific factories.
|
|
5
|
+
*/
|
|
6
|
+
export type { LogFn } from "@locusai/sdk/node";
|
|
7
|
+
export { createWorkerLogger, noopLogger } from "@locusai/sdk/node";
|
|
8
|
+
/**
|
|
9
|
+
* Creates a CLI-style logger with colored icons.
|
|
10
|
+
* Used by CLI commands (discuss, plan, docs, review) for consistent output.
|
|
11
|
+
*
|
|
12
|
+
* Output format: ` ● message` (with level-appropriate icon and color)
|
|
13
|
+
*/
|
|
14
|
+
export declare function createCliLogger(): (message: string, level?: "info" | "success" | "warn" | "error") => void;
|
|
15
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEnE;;;;;GAKG;AACH,wBAAgB,eAAe,KACrB,SAAS,MAAM,EAAE,QAAQ,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,UAWvE"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized logging utilities for Locus packages.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports core logger types from SDK and adds CLI-specific factories.
|
|
5
|
+
*/
|
|
6
|
+
import { c } from "@locusai/sdk/node";
|
|
7
|
+
// Re-export core logging primitives from SDK
|
|
8
|
+
export { createWorkerLogger, noopLogger } from "@locusai/sdk/node";
|
|
9
|
+
/**
|
|
10
|
+
* Creates a CLI-style logger with colored icons.
|
|
11
|
+
* Used by CLI commands (discuss, plan, docs, review) for consistent output.
|
|
12
|
+
*
|
|
13
|
+
* Output format: ` ● message` (with level-appropriate icon and color)
|
|
14
|
+
*/
|
|
15
|
+
export function createCliLogger() {
|
|
16
|
+
return (message, level) => {
|
|
17
|
+
const icon = level === "success"
|
|
18
|
+
? c.success("✔")
|
|
19
|
+
: level === "error"
|
|
20
|
+
? c.error("✖")
|
|
21
|
+
: level === "warn"
|
|
22
|
+
? c.warning("!")
|
|
23
|
+
: c.info("●");
|
|
24
|
+
console.log(` ${icon} ${message}`);
|
|
25
|
+
};
|
|
26
|
+
}
|
package/dist/plans.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PlanManager, SprintPlan } from "@locusai/sdk/node";
|
|
2
|
+
export interface PlanSummary {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
status: string;
|
|
6
|
+
taskCount: number;
|
|
7
|
+
directive: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
feedback?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* List all plans with summary info.
|
|
13
|
+
*/
|
|
14
|
+
export declare function listPlans(manager: PlanManager, status?: SprintPlan["status"]): PlanSummary[];
|
|
15
|
+
/**
|
|
16
|
+
* Get plan markdown content. Returns null if not found.
|
|
17
|
+
*/
|
|
18
|
+
export declare function showPlan(manager: PlanManager, idOrSlug: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Cancel a plan by ID. Throws if not found.
|
|
21
|
+
*/
|
|
22
|
+
export declare function cancelPlan(manager: PlanManager, idOrSlug: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Reject a plan with feedback. Throws if not found or not pending.
|
|
25
|
+
* Returns the rejected plan.
|
|
26
|
+
*/
|
|
27
|
+
export declare function rejectPlan(manager: PlanManager, idOrSlug: string, feedback: string): SprintPlan;
|
|
28
|
+
//# sourceMappingURL=plans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,GAC5B,WAAW,EAAE,CAWf;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAEf;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEvE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,UAAU,CAEZ"}
|
package/dist/plans.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List all plans with summary info.
|
|
3
|
+
*/
|
|
4
|
+
export function listPlans(manager, status) {
|
|
5
|
+
const plans = manager.list(status);
|
|
6
|
+
return plans.map((p) => ({
|
|
7
|
+
id: p.id,
|
|
8
|
+
name: p.name,
|
|
9
|
+
status: p.status,
|
|
10
|
+
taskCount: p.tasks.length,
|
|
11
|
+
directive: p.directive,
|
|
12
|
+
createdAt: p.createdAt,
|
|
13
|
+
feedback: p.feedback,
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get plan markdown content. Returns null if not found.
|
|
18
|
+
*/
|
|
19
|
+
export function showPlan(manager, idOrSlug) {
|
|
20
|
+
return manager.getMarkdown(idOrSlug);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Cancel a plan by ID. Throws if not found.
|
|
24
|
+
*/
|
|
25
|
+
export function cancelPlan(manager, idOrSlug) {
|
|
26
|
+
manager.cancel(idOrSlug);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reject a plan with feedback. Throws if not found or not pending.
|
|
30
|
+
* Returns the rejected plan.
|
|
31
|
+
*/
|
|
32
|
+
export function rejectPlan(manager, idOrSlug, feedback) {
|
|
33
|
+
return manager.reject(idOrSlug, feedback);
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface TelegramSettings {
|
|
2
|
+
botToken?: string;
|
|
3
|
+
chatId?: number;
|
|
4
|
+
testMode?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface LocusSettings {
|
|
7
|
+
$schema?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
provider?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
workspaceId?: string;
|
|
13
|
+
telegram?: TelegramSettings;
|
|
14
|
+
}
|
|
15
|
+
export declare class SettingsManager {
|
|
16
|
+
private projectPath;
|
|
17
|
+
constructor(projectPath: string);
|
|
18
|
+
load(): LocusSettings;
|
|
19
|
+
save(settings: LocusSettings): void;
|
|
20
|
+
get<K extends keyof LocusSettings>(key: K): LocusSettings[K];
|
|
21
|
+
set<K extends keyof LocusSettings>(key: K, value: LocusSettings[K]): void;
|
|
22
|
+
remove(): void;
|
|
23
|
+
exists(): boolean;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAMD,qBAAa,eAAe;IACd,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEvC,IAAI,IAAI,aAAa;IAQrB,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAOnC,GAAG,CAAC,CAAC,SAAS,MAAM,aAAa,EAAE,GAAG,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;IAI5D,GAAG,CAAC,CAAC,SAAS,MAAM,aAAa,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI;IAMzE,MAAM,IAAI,IAAI;IAOd,MAAM,IAAI,OAAO;CAGlB"}
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { LOCUS_CONFIG, LOCUS_SCHEMAS } from "@locusai/sdk/node";
|
|
4
|
+
function getSettingsPath(projectPath) {
|
|
5
|
+
return join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
6
|
+
}
|
|
7
|
+
export class SettingsManager {
|
|
8
|
+
projectPath;
|
|
9
|
+
constructor(projectPath) {
|
|
10
|
+
this.projectPath = projectPath;
|
|
11
|
+
}
|
|
12
|
+
load() {
|
|
13
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
14
|
+
if (!existsSync(settingsPath)) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
return JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
18
|
+
}
|
|
19
|
+
save(settings) {
|
|
20
|
+
const { $schema: _, ...rest } = settings;
|
|
21
|
+
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
22
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
23
|
+
writeFileSync(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
24
|
+
}
|
|
25
|
+
get(key) {
|
|
26
|
+
return this.load()[key];
|
|
27
|
+
}
|
|
28
|
+
set(key, value) {
|
|
29
|
+
const settings = this.load();
|
|
30
|
+
settings[key] = value;
|
|
31
|
+
this.save(settings);
|
|
32
|
+
}
|
|
33
|
+
remove() {
|
|
34
|
+
const settingsPath = getSettingsPath(this.projectPath);
|
|
35
|
+
if (existsSync(settingsPath)) {
|
|
36
|
+
unlinkSync(settingsPath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exists() {
|
|
40
|
+
return existsSync(getSettingsPath(this.projectPath));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LocusClient } from "@locusai/sdk";
|
|
2
|
+
export interface WorkspaceResolverOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
apiBase: string;
|
|
5
|
+
workspaceId?: string;
|
|
6
|
+
log?: (message: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare class WorkspaceResolver {
|
|
9
|
+
private options;
|
|
10
|
+
constructor(options: WorkspaceResolverOptions);
|
|
11
|
+
resolve(): Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Convenience function to resolve workspace ID.
|
|
15
|
+
* If workspaceId is provided, returns it directly.
|
|
16
|
+
* Otherwise resolves from API key info.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveWorkspaceId(client: LocusClient, workspaceId?: string): Promise<string>;
|
|
19
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,wBAAwB;IAE/C,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;CAiCjC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LocusClient } from "@locusai/sdk";
|
|
2
|
+
import { noopLogger } from "./logger.js";
|
|
3
|
+
export class WorkspaceResolver {
|
|
4
|
+
options;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async resolve() {
|
|
9
|
+
if (this.options.workspaceId) {
|
|
10
|
+
return this.options.workspaceId;
|
|
11
|
+
}
|
|
12
|
+
const log = this.options.log ?? noopLogger;
|
|
13
|
+
try {
|
|
14
|
+
log("Resolving workspace from API key...");
|
|
15
|
+
const client = new LocusClient({
|
|
16
|
+
baseUrl: this.options.apiBase,
|
|
17
|
+
token: this.options.apiKey,
|
|
18
|
+
});
|
|
19
|
+
const info = await client.auth.getApiKeyInfo();
|
|
20
|
+
if (info.workspaceId) {
|
|
21
|
+
log(`Resolved workspace: ${info.workspaceId}`);
|
|
22
|
+
return info.workspaceId;
|
|
23
|
+
}
|
|
24
|
+
throw new Error("API key is not associated with a workspace. Please specify a workspace ID.");
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error instanceof Error && error.message.includes("API key is not")) {
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Error resolving workspace: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Convenience function to resolve workspace ID.
|
|
36
|
+
* If workspaceId is provided, returns it directly.
|
|
37
|
+
* Otherwise resolves from API key info.
|
|
38
|
+
*/
|
|
39
|
+
export async function resolveWorkspaceId(client, workspaceId) {
|
|
40
|
+
if (workspaceId) {
|
|
41
|
+
return workspaceId;
|
|
42
|
+
}
|
|
43
|
+
const info = await client.auth.getApiKeyInfo();
|
|
44
|
+
if (info.workspaceId) {
|
|
45
|
+
return info.workspaceId;
|
|
46
|
+
}
|
|
47
|
+
throw new Error("Could not resolve workspace from API key. Please set workspaceId in settings.");
|
|
48
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@locusai/commands",
|
|
3
|
+
"version": "0.16.2",
|
|
4
|
+
"description": "Shared command logic for Locus CLI and Telegram bot",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node --format cjs --external @locusai/sdk --external @locusai/shared --external zod && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
14
|
+
"lint": "biome lint .",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"clean": "rm -rf node_modules"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@locusai/sdk": "^0.16.2",
|
|
20
|
+
"@locusai/shared": "^0.16.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.8.3",
|
|
24
|
+
"@types/node": "^22.10.7"
|
|
25
|
+
}
|
|
26
|
+
}
|