@lmctl-ai/lmctl 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +466 -457
- package/dist/cli/ratelimit.d.ts +0 -1
- package/dist/cli/script.js +239 -227
- package/dist/cli/seed-flight.d.ts +2 -0
- package/dist/cli/teamfile-compat.d.ts +25 -0
- package/dist/providers/adapters/agy-adapter.d.ts +2 -1
- package/dist/providers/base-adapter.d.ts +3 -0
- package/dist/providers/config/model-selection-lint.d.ts +0 -1
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/ratelimit/agy.d.ts +21 -0
- package/dist/providers/ratelimit/claude.d.ts +5 -0
- package/dist/providers/ratelimit/codex.d.ts +6 -0
- package/dist/providers/ratelimit/kimi.d.ts +5 -0
- package/dist/providers/ratelimit/schema.d.ts +14 -0
- package/dist/providers/ratelimit-readings.d.ts +1 -16
- package/examples/book-team.lmctl +16 -12
- package/examples/research-team.lmctl +16 -12
- package/examples/review-team.lmctl +17 -12
- package/package.json +2 -3
- package/durable-memory/model_selection.md +0 -381
|
@@ -48,6 +48,31 @@ export declare function _setTeamfileSeedForTests(fn: SeedFn | null): void;
|
|
|
48
48
|
export declare function _setLintSessiondirFsOpsForTests(ops: Partial<LintSessiondirFsOps> | null): void;
|
|
49
49
|
export declare function _setLintOpenCodeConfigProbeForTests(fn: OpenCodeConfigProbeFn | null): void;
|
|
50
50
|
export declare function runLint(args: string[], io?: TeamfileCliIo): Promise<number>;
|
|
51
|
+
export interface LintFinding {
|
|
52
|
+
/** Real member alias the finding is about, or "" when it's about the file itself. */
|
|
53
|
+
alias: string;
|
|
54
|
+
message: string;
|
|
55
|
+
}
|
|
56
|
+
export interface LintStructuredResult {
|
|
57
|
+
/** "error" ONLY for a genuine validation error (unparseable file, bad
|
|
58
|
+
* _MEMBER_ line, confirmed wrong-provider model). Warnings never flip
|
|
59
|
+
* this to "error" -- matches the CLI's own exit-0-on-warnings contract. */
|
|
60
|
+
status: 'ok' | 'error';
|
|
61
|
+
errors: LintFinding[];
|
|
62
|
+
warnings: LintFinding[];
|
|
63
|
+
counts: {
|
|
64
|
+
errors: number;
|
|
65
|
+
warnings: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Structured form of the exact same lint computation `lmctl lint` runs
|
|
70
|
+
* (queue#22208): a workflow reads errors/warnings/counts directly instead
|
|
71
|
+
* of string-matching "warning:"-prefixed lines, which breaks silently the
|
|
72
|
+
* day that prefix changes -- the failure shape math named as the reason
|
|
73
|
+
* this exists. Both lists are always arrays, never null/undefined.
|
|
74
|
+
*/
|
|
75
|
+
export declare function lintTeamfileStructured(teamfile: string, io: TeamfileCliIo): Promise<LintStructuredResult>;
|
|
51
76
|
export declare function runSeed(args: string[], io?: TeamfileCliIo): Promise<number>;
|
|
52
77
|
/**
|
|
53
78
|
* Per-alias seed prompt override (lmscript's lm_seed, operator 2026-09-30:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseProviderAdapter } from "../base-adapter.js";
|
|
2
|
-
import type { SeedOptions } from "../adapter.js";
|
|
2
|
+
import type { SeedOptions, SeedResult } from "../adapter.js";
|
|
3
3
|
export declare function createAgyAdapter(opts?: {
|
|
4
4
|
agyDir?: string;
|
|
5
5
|
}): AgyAdapter;
|
|
@@ -8,6 +8,7 @@ declare class AgyAdapter extends BaseProviderAdapter {
|
|
|
8
8
|
agyDir?: string;
|
|
9
9
|
});
|
|
10
10
|
protected seedArgsFor(opts?: SeedOptions): string[];
|
|
11
|
+
seed(cwd: string, opts?: SeedOptions): Promise<SeedResult>;
|
|
11
12
|
manualSeedHint(sessiondir: string): {
|
|
12
13
|
launchCommand: string;
|
|
13
14
|
postLaunchNote: string;
|
|
@@ -36,6 +36,8 @@ export interface BaseAdapterConfig {
|
|
|
36
36
|
binary: string;
|
|
37
37
|
/** Args for seeding a new session with the explicit seed instruction. */
|
|
38
38
|
seedArgs: string[];
|
|
39
|
+
/** Process timeout for the one-shot seed command. */
|
|
40
|
+
seedTimeoutMs?: number;
|
|
39
41
|
/** If seed input should be sent via stdin instead of args. */
|
|
40
42
|
seedStdin?: string;
|
|
41
43
|
/** Default resume command template. {{session_id}} is replaced. */
|
|
@@ -54,6 +56,7 @@ export declare class BaseProviderAdapter implements ProviderAdapter {
|
|
|
54
56
|
protected provider: SessionProvider;
|
|
55
57
|
protected binary: string;
|
|
56
58
|
protected seedArgs: string[];
|
|
59
|
+
protected seedTimeoutMs: number;
|
|
57
60
|
protected seedStdin?: string;
|
|
58
61
|
protected resumeTemplate: string;
|
|
59
62
|
protected startTemplate: string;
|
|
@@ -32,4 +32,3 @@ export declare function catalogModelWarnings(members: MemberConfig[], opts?: {
|
|
|
32
32
|
export declare function catalogModelErrors(members: MemberConfig[], opts?: {
|
|
33
33
|
snapshot?: CatalogSnapshot;
|
|
34
34
|
}): Promise<TeamValidationError[]>;
|
|
35
|
-
export declare function resolveModelSelectionDocPathFromHere(here: string): string;
|
|
@@ -335,6 +335,8 @@ export interface ListSessionsOptions {
|
|
|
335
335
|
* ```
|
|
336
336
|
*/
|
|
337
337
|
export declare function listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
|
|
338
|
+
/** Delete one native session from a known provider. */
|
|
339
|
+
export declare function deleteSession(provider: ProviderName, sessionId: string, registry?: ProviderRegistry): Promise<boolean>;
|
|
338
340
|
/**
|
|
339
341
|
* Options for `countTokens()`.
|
|
340
342
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type CommandResult = {
|
|
2
|
+
status: number | null;
|
|
3
|
+
stdout?: string;
|
|
4
|
+
error?: NodeJS.ErrnoException;
|
|
5
|
+
};
|
|
6
|
+
type CommandRunner = (timeoutMs: number, env: NodeJS.ProcessEnv) => CommandResult;
|
|
7
|
+
type HttpResult = {
|
|
8
|
+
status: number;
|
|
9
|
+
body: string;
|
|
10
|
+
};
|
|
11
|
+
type HttpRunner = (url: string, headers: Record<string, string>, timeoutMs: number) => Promise<HttpResult>;
|
|
12
|
+
/** Test seam; production callers should use readAgy. */
|
|
13
|
+
export declare function _setAgyDepsForTests(deps: {
|
|
14
|
+
command?: CommandRunner;
|
|
15
|
+
http?: HttpRunner;
|
|
16
|
+
} | null): void;
|
|
17
|
+
export declare function readAgy(options: {
|
|
18
|
+
timeoutSeconds: number;
|
|
19
|
+
env: NodeJS.ProcessEnv;
|
|
20
|
+
}): Promise<Record<string, unknown>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Wire format shared by the in-process subscription quota readers. */
|
|
2
|
+
export declare const SCHEMA_URL = "https://lmctl.com/schemas/lmratelimit-v1.json";
|
|
3
|
+
export declare function clampFraction(value: unknown): number | null;
|
|
4
|
+
export declare function toFraction(value: unknown, scale: 'fraction' | 'percent'): number | null;
|
|
5
|
+
export declare function isoFromEpoch(value: unknown): string | null;
|
|
6
|
+
export declare function epochFromIso(value: unknown): number | null;
|
|
7
|
+
export declare function windowTypeFromMinutes(value: unknown): string;
|
|
8
|
+
export declare function windowTypeFromLabel(value: unknown): string;
|
|
9
|
+
export declare function windowSuffix(type: string): string;
|
|
10
|
+
export declare function redact(value: unknown, secrets?: Iterable<string | null | undefined>): string;
|
|
11
|
+
export declare function makeBucket(fields: Record<string, unknown>): Record<string, unknown>;
|
|
12
|
+
export declare function makeReading(provider: string, status: string, fields?: Record<string, unknown>): Record<string, unknown>;
|
|
13
|
+
export declare function errorReading(provider: string, status: string, reason: string, detail?: unknown, source?: string, secrets?: Iterable<string | null | undefined>, retryAfterSeconds?: number | null): Record<string, unknown>;
|
|
14
|
+
export declare function parseRetryAfter(headers: Headers): number | null;
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
export declare const VALID_RATELIMIT_TARGETS: readonly ["all", "codex", "agy", "claude", "kimi"];
|
|
2
|
-
export interface RatelimitRunResult {
|
|
3
|
-
status: number | null;
|
|
4
|
-
stdout: string;
|
|
5
|
-
stderr: string;
|
|
6
|
-
signal?: string | null;
|
|
7
|
-
error?: (Error & {
|
|
8
|
-
code?: string;
|
|
9
|
-
}) | undefined;
|
|
10
|
-
}
|
|
11
|
-
export type RatelimitRunner = (bin: string, args: string[], timeoutMs: number) => RatelimitRunResult;
|
|
12
|
-
export interface RatelimitFsDeps {
|
|
13
|
-
exists(path: string): boolean;
|
|
14
|
-
}
|
|
15
|
-
export declare function _setRatelimitRunnerForTests(fn: RatelimitRunner | null): void;
|
|
16
|
-
export declare function _setRatelimitFsDepsForTests(deps: Partial<RatelimitFsDeps> | null): void;
|
|
17
2
|
export interface RatelimitReadingsOptions {
|
|
18
3
|
providers?: string[];
|
|
19
4
|
targets?: string[];
|
|
@@ -29,4 +14,4 @@ export type RatelimitReadingsResult = {
|
|
|
29
14
|
error: string;
|
|
30
15
|
readings: [];
|
|
31
16
|
};
|
|
32
|
-
export declare function queryRatelimitReadings(options?: RatelimitReadingsOptions): RatelimitReadingsResult
|
|
17
|
+
export declare function queryRatelimitReadings(options?: RatelimitReadingsOptions): Promise<RatelimitReadingsResult>;
|
package/examples/book-team.lmctl
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
# Book Team
|
|
2
2
|
|
|
3
|
-
# Use this team for long-form writing projects.
|
|
4
|
-
# acceptance criteria
|
|
5
|
-
# checks. Keep chapter plans and
|
|
6
|
-
# sessions can recover the book state.
|
|
7
|
-
#
|
|
8
|
-
# lmctl
|
|
3
|
+
# Use this team for long-form writing projects. A suggested way to USE it: keep
|
|
4
|
+
# the outline and acceptance criteria in durable-memory/, then prompt members in
|
|
5
|
+
# turn for drafting, editing and factual checks. Keep chapter plans and
|
|
6
|
+
# decisions in durable-memory/ so refreshed sessions can recover the book state.
|
|
7
|
+
# You drive that sequence -- prompt each member:
|
|
8
|
+
# lmctl prompt book-team.lmctl Drafter "Draft chapter 1 from durable-memory/outline.md"
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
10
|
+
# ALIASES ARE NAMES, NOT CAPABILITIES. Every alias below is a label you chose.
|
|
11
|
+
# lmctl gives none of them any power the others lack, and a seeded member is
|
|
12
|
+
# told only to establish its session -- it does not know lmctl exists or that
|
|
13
|
+
# the other members are there. The difference between them is how YOU use them.
|
|
14
|
+
# To let one send prompts to others, paste that guidance into its session
|
|
15
|
+
# yourself; give the same text to any other member and it can do it too.
|
|
16
|
+
#
|
|
17
|
+
# lmctl prompt blocks until the member replies. If the member is already running
|
|
18
|
+
# a turn you get "<alias> is busy; wait and retry" -- nothing is held for you,
|
|
19
|
+
# so retrying is the caller's job.
|
|
16
20
|
#
|
|
17
21
|
# For async, lmctl is foreground/background agnostic: wrap the same command in
|
|
18
22
|
# your provider or host runtime's native background facility (e.g. Claude
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
# Research Team
|
|
2
2
|
|
|
3
|
-
# Use this team when
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# lmctl
|
|
3
|
+
# Use this team when you need a researched answer with clear caveats. A
|
|
4
|
+
# suggested way to USE it: ask each researcher for an independent pass, ask
|
|
5
|
+
# Critic to find weak evidence or contradictions, then ask Synthesizer to merge
|
|
6
|
+
# the strongest findings. You drive that sequence -- prompt each member:
|
|
7
|
+
# lmctl prompt research-team.lmctl Researcher1 "Research <question>"
|
|
8
|
+
# lmctl prompt research-team.lmctl Critic "Find weak evidence in <path>"
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
10
|
+
# ALIASES ARE NAMES, NOT CAPABILITIES. Every alias below is a label you chose.
|
|
11
|
+
# lmctl gives none of them any power the others lack, and a seeded member is
|
|
12
|
+
# told only to establish its session -- it does not know lmctl exists or that
|
|
13
|
+
# the other members are there. The difference between them is how YOU use them.
|
|
14
|
+
# To let one send prompts to others, paste that guidance into its session
|
|
15
|
+
# yourself; give the same text to any other member and it can do it too.
|
|
16
|
+
#
|
|
17
|
+
# lmctl prompt blocks until the member replies. If the member is already running
|
|
18
|
+
# a turn you get "<alias> is busy; wait and retry" -- nothing is held for you,
|
|
19
|
+
# so retrying is the caller's job.
|
|
16
20
|
#
|
|
17
21
|
# For async, lmctl is foreground/background agnostic: wrap the same command in
|
|
18
22
|
# your provider or host runtime's native background facility (e.g. Claude
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
# Review Team
|
|
2
2
|
|
|
3
|
-
# Use this team for implementation tasks that need independent review.
|
|
4
|
-
#
|
|
5
|
-
# reviews,
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# lmctl
|
|
3
|
+
# Use this team for implementation tasks that need independent review. A
|
|
4
|
+
# suggested way to USE it: send implementation work to Author, ask Reviewer1 and
|
|
5
|
+
# Reviewer2 for separate reviews, ask SecurityReviewer about trust boundaries
|
|
6
|
+
# when relevant, then send the findings back to Author before signoff.
|
|
7
|
+
# You drive that sequence -- prompt each member yourself:
|
|
8
|
+
# lmctl prompt review-team.lmctl Author "Implement <task>"
|
|
9
|
+
# lmctl prompt review-team.lmctl Reviewer1 "Review the change in <path>"
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
11
|
+
# ALIASES ARE NAMES, NOT CAPABILITIES. Every alias below is a label you chose.
|
|
12
|
+
# lmctl gives none of them any power the others lack, and a seeded member is
|
|
13
|
+
# told only to establish its session -- it does not know lmctl exists or that
|
|
14
|
+
# the other members are there. The difference between them is how YOU use them.
|
|
15
|
+
# To let one send prompts to others, paste that guidance into its session
|
|
16
|
+
# yourself; give the same text to any other member and it can do it too.
|
|
17
|
+
#
|
|
18
|
+
# lmctl prompt blocks until the member replies. If the member is already running
|
|
19
|
+
# a turn you get "<alias> is busy; wait and retry" -- nothing is held for you,
|
|
20
|
+
# so retrying is the caller's job.
|
|
16
21
|
#
|
|
17
22
|
# For async, lmctl is foreground/background agnostic: wrap the same command in
|
|
18
23
|
# your provider or host runtime's native background facility (e.g. Claude
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmctl-ai/lmctl",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
4
4
|
"description": "A provider-agnostic control plane for teams of AI coding agents — across providers, with independent review and durable memory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://lmctl.com",
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"dist/**/*.d.ts",
|
|
22
22
|
"dist/cli/schema.sql",
|
|
23
23
|
"dist/config/",
|
|
24
|
-
"durable-memory/model_selection.md",
|
|
25
24
|
"data/price-data.json",
|
|
26
25
|
"data/price-aliases.json",
|
|
27
26
|
"workflows/",
|
|
@@ -38,7 +37,7 @@
|
|
|
38
37
|
"license": "SEE LICENSE IN LICENSE",
|
|
39
38
|
"publishConfig": {
|
|
40
39
|
"access": "public",
|
|
41
|
-
"tag": "
|
|
40
|
+
"tag": "staging"
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {},
|
|
44
43
|
"optionalDependencies": {}
|