@kisev/agentomatic 11.0.2 → 12.0.0-dev.113.g1bd7385234c6
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/README.md +32 -16
- package/README.ru.md +33 -16
- package/dist/agent-profiles.d.ts +8 -2
- package/dist/agent-profiles.js +131 -82
- package/dist/assets/agents/architect.md +4 -4
- package/dist/assets/agents/critic.md +10 -11
- package/dist/assets/agents/manager.md +11 -12
- package/dist/assets/agents/mapper.md +4 -4
- package/dist/assets/agents/review.md +11 -12
- package/dist/assets/agents/worker.md +29 -32
- package/dist/assets/commands/asd-ste100.md +10 -0
- package/dist/assets/commands/code-simplify.md +12 -0
- package/dist/assets/commands/eli5.md +10 -0
- package/dist/assets/commands/handoff.md +10 -0
- package/dist/assets/commands/rtk-stats.md +2 -2
- package/dist/assets/commands/skill-doctor.md +10 -0
- package/dist/assets/migration-inventory.json +17 -6
- package/dist/assets/plugins/code-simplify.js +3 -0
- package/dist/assets/plugins/rtk.js +1 -1
- package/dist/assets/plugins/rules-injector.js +1 -1
- package/dist/assets/plugins/zed-bell.js +1 -1
- package/dist/catalog.d.ts +2 -2
- package/dist/catalog.js +9 -3
- package/dist/cli-output.js +6 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1257
- package/dist/command-cli.d.ts +1 -0
- package/dist/command-cli.js +1004 -0
- package/dist/config-setup.d.ts +14 -8
- package/dist/config-setup.js +197 -64
- package/dist/contracts.js +26 -0
- package/dist/doctor.d.ts +0 -1
- package/dist/doctor.js +42 -55
- package/dist/index.d.ts +13 -118
- package/dist/index.js +118 -125
- package/dist/installer.d.ts +25 -2
- package/dist/installer.js +68 -23
- package/dist/jsonc.d.ts +18 -2
- package/dist/jsonc.js +109 -5
- package/dist/opencode-config.d.ts +11 -0
- package/dist/opencode-config.js +237 -0
- package/dist/plugins/code-simplify.d.ts +25 -0
- package/dist/plugins/code-simplify.js +49 -0
- package/dist/plugins/events.d.ts +20 -0
- package/dist/plugins/events.js +48 -0
- package/dist/plugins/rtk.d.ts +9 -12
- package/dist/plugins/rtk.js +20 -5
- package/dist/plugins/rules-injector.d.ts +21 -11
- package/dist/plugins/rules-injector.js +43 -31
- package/dist/plugins/zed-bell.d.ts +8 -5
- package/dist/plugins/zed-bell.js +10 -2
- package/dist/reconcile.d.ts +4 -1
- package/dist/reconcile.js +12 -16
- package/dist/registry.d.ts +1 -0
- package/dist/registry.js +21 -5
- package/dist/routing.d.ts +5 -3
- package/dist/routing.js +57 -22
- package/dist/self-install.d.ts +7 -0
- package/dist/self-install.js +52 -1
- package/dist/terminal-wizard.d.ts +1 -1
- package/dist/terminal-wizard.js +2 -2
- package/package.json +12 -7
- package/dist/assets/commands/skill-improve.md +0 -10
- package/dist/assets/commands/stopit.md +0 -10
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const CODE_SIMPLIFY_ROLES = [
|
|
2
|
+
"manager",
|
|
3
|
+
"architect",
|
|
4
|
+
"mapper",
|
|
5
|
+
"worker",
|
|
6
|
+
"review",
|
|
7
|
+
"critic",
|
|
8
|
+
];
|
|
9
|
+
const LADDER = `Prevention ladder for the code being written or changed, applied in order; stop at the first step that satisfies the agreed need: 1) necessity; 2) existing code; 3) standard library; 4) native platform; 5) installed dependency; 6) one line; 7) minimum. The ladder applies to the current change only; never scan repositories or unrelated files — a targeted caller search by exact name for code you are changing is not scanning. Fix the caller in the same change when that is the smaller diff. A conscious cut records a one-line SIMPLIFY marker (ceiling -> upgrade trigger) on the added or changed code in the same edit. Keep new verification proportionate: one runnable check for logic that is not evidently correct, none for a trivial one-liner. Never cancel a clarification or confirmation gate.`;
|
|
10
|
+
const AUDIT = `Complexity audit only on an explicit request, only in the requested scope, always report-only. Findings are ranked one-liners: exact location, one tag of delete/stdlib/native/reuse/yagni/shrink, and the concrete simpler alternative that preserves behavior. Collect SIMPLIFY debt markers in the scope into a separate registry section, never tagged findings: location, ceiling, trigger; mark a marker no-trigger when its upgrade trigger has not fired.`;
|
|
11
|
+
const GUARDS = `Search every usage, including dynamic references, before any delete finding. Never propose simplification that crosses a trust boundary, risks data loss, weakens security or accessibility, drops error handling that hides failures, or removes explicitly requested behavior.`;
|
|
12
|
+
function rules(level) {
|
|
13
|
+
const parts = level === "lite" ? [LADDER] : level === "full" ? [LADDER, AUDIT] : [LADDER, AUDIT, GUARDS];
|
|
14
|
+
return `[code-simplify level=${level}]\n${parts.join("\n\n")}\n[/code-simplify]`;
|
|
15
|
+
}
|
|
16
|
+
function roleOf(agent) {
|
|
17
|
+
if (agent && CODE_SIMPLIFY_ROLES.includes(agent))
|
|
18
|
+
return agent;
|
|
19
|
+
return agent?.startsWith("critic-") ? "critic" : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function codeSimplify(options = {}) {
|
|
22
|
+
const level = options.level ?? "full";
|
|
23
|
+
if (level === "off")
|
|
24
|
+
return {};
|
|
25
|
+
if (options.scope?.some((role) => !CODE_SIMPLIFY_ROLES.includes(role)))
|
|
26
|
+
throw new Error(`code-simplify scope must list roles from ${CODE_SIMPLIFY_ROLES.join(", ")}`);
|
|
27
|
+
const scope = options.scope ? new Set(options.scope) : undefined;
|
|
28
|
+
const text = rules(level);
|
|
29
|
+
return {
|
|
30
|
+
context: async (input) => {
|
|
31
|
+
if (scope) {
|
|
32
|
+
const role = roleOf(input.agent);
|
|
33
|
+
if (!role || !scope.has(role))
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (input.system.some((part) => part.type === "text" && part.text === text))
|
|
37
|
+
return;
|
|
38
|
+
input.system.push({ type: "text", text });
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export default {
|
|
43
|
+
id: "agentomatic.code-simplify",
|
|
44
|
+
async setup(ctx) {
|
|
45
|
+
const hooks = await codeSimplify(ctx.options);
|
|
46
|
+
if (hooks.context)
|
|
47
|
+
await ctx.session.hook("context", hooks.context);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
2
|
+
import type { Result } from "@opencode/plugin/promise/tool";
|
|
3
|
+
export type ToolAfter = {
|
|
4
|
+
tool: string;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
input: unknown;
|
|
7
|
+
} & ({
|
|
8
|
+
status: "completed";
|
|
9
|
+
result: Result;
|
|
10
|
+
} | {
|
|
11
|
+
status: "error";
|
|
12
|
+
error: unknown;
|
|
13
|
+
});
|
|
14
|
+
export declare function toolInput(value: unknown): Record<string, unknown>;
|
|
15
|
+
export declare function resultText(result: Result): string | undefined;
|
|
16
|
+
export declare function replaceResultText(result: Result, text: string): Result;
|
|
17
|
+
export declare function subscribeEvents(ctx: Plugin.Context, receive: (event: {
|
|
18
|
+
type: string;
|
|
19
|
+
data?: unknown;
|
|
20
|
+
}) => Promise<void>): () => Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function toolInput(value) {
|
|
2
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: {};
|
|
5
|
+
}
|
|
6
|
+
export function resultText(result) {
|
|
7
|
+
if (typeof result.content === "string")
|
|
8
|
+
return result.content;
|
|
9
|
+
if (Array.isArray(result.content))
|
|
10
|
+
return result.content
|
|
11
|
+
.filter((part) => part.type === "text")
|
|
12
|
+
.map((part) => part.text)
|
|
13
|
+
.join("\n");
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export function replaceResultText(result, text) {
|
|
17
|
+
if (!Array.isArray(result.content))
|
|
18
|
+
return { ...result, content: text };
|
|
19
|
+
let replaced = false;
|
|
20
|
+
return {
|
|
21
|
+
...result,
|
|
22
|
+
content: result.content.flatMap((part) => {
|
|
23
|
+
if (part.type !== "text")
|
|
24
|
+
return [part];
|
|
25
|
+
if (replaced)
|
|
26
|
+
return [];
|
|
27
|
+
replaced = true;
|
|
28
|
+
return [{ type: "text", text }];
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function subscribeEvents(ctx, receive) {
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
const subscription = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
for await (const event of ctx.event.subscribe({ signal: controller.signal }))
|
|
37
|
+
await receive(event);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (!controller.signal.aborted)
|
|
41
|
+
console.error("agentomatic event subscription failed", error);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
return async () => {
|
|
45
|
+
controller.abort();
|
|
46
|
+
await subscription;
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/plugins/rtk.d.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
args?: {
|
|
4
|
-
command?: unknown;
|
|
5
|
-
};
|
|
6
|
-
};
|
|
7
|
-
type HookOutput = {
|
|
8
|
-
output?: unknown;
|
|
9
|
-
};
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
2
|
+
import { type ToolAfter } from "./events.js";
|
|
10
3
|
export type RtkMethod = "compressed-rtk" | "truncated-head-tail" | "rtk-unavailable" | "ineligible" | "below-threshold";
|
|
11
4
|
export type RtkStats = {
|
|
12
5
|
schema_version: 1;
|
|
@@ -34,8 +27,12 @@ export declare function rtkCharsSaved(stats: RtkStats): number;
|
|
|
34
27
|
export declare function rtkTokensSavedEstimate(stats: RtkStats): number;
|
|
35
28
|
export declare function readRtkStats(path?: string): Promise<RtkStats | undefined>;
|
|
36
29
|
export declare function rtk(options?: RtkOptions): Promise<{
|
|
37
|
-
"
|
|
30
|
+
"execute.after"?: undefined;
|
|
38
31
|
} | {
|
|
39
|
-
"
|
|
32
|
+
"execute.after": (input: ToolAfter) => Promise<void>;
|
|
40
33
|
}>;
|
|
41
|
-
|
|
34
|
+
declare const _default: {
|
|
35
|
+
id: string;
|
|
36
|
+
setup(ctx: Plugin.Context): Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
export default _default;
|
package/dist/plugins/rtk.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resultText, replaceResultText, toolInput } from "./events.js";
|
|
1
2
|
import { spawn } from "node:child_process";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { homedir } from "node:os";
|
|
@@ -137,26 +138,33 @@ export async function rtk(options = {}) {
|
|
|
137
138
|
((selected, input) => external(options.bin ?? "rtk", selected, input));
|
|
138
139
|
const statsPath = options.statsPath === null ? undefined : (options.statsPath ?? rtkStatsPath());
|
|
139
140
|
return {
|
|
140
|
-
"
|
|
141
|
+
"execute.after": async (input) => {
|
|
142
|
+
if (input.status !== "completed")
|
|
143
|
+
return;
|
|
144
|
+
const output = { output: resultText(input.result) };
|
|
141
145
|
try {
|
|
142
146
|
if (input.tool === "edit" &&
|
|
143
147
|
typeof output.output === "string" &&
|
|
144
148
|
/oldString (not found|found multiple times|and newString must be different)/i.test(output.output))
|
|
145
149
|
output.output = `${output.output}\nSTOP. Read the file before retrying Edit.`;
|
|
146
|
-
if (input.tool !== "
|
|
150
|
+
if (input.tool !== "shell" ||
|
|
147
151
|
typeof output.output !== "string" ||
|
|
148
152
|
output.output.length < (options.threshold ?? THRESHOLD)) {
|
|
149
|
-
if (input.tool === "
|
|
153
|
+
if (input.tool === "shell" && typeof output.output === "string" && statsPath)
|
|
150
154
|
await recordStats(statsPath, "below-threshold", output.output.length, output.output.length);
|
|
155
|
+
if (output.output !== undefined)
|
|
156
|
+
input.result = replaceResultText(input.result, output.output);
|
|
151
157
|
return;
|
|
152
158
|
}
|
|
153
|
-
const
|
|
159
|
+
const command = toolInput(input.input).command;
|
|
160
|
+
const selected = typeof command === "string" ? filter(command) : undefined;
|
|
154
161
|
const compressed = selected ? await run(selected, output.output) : undefined;
|
|
155
162
|
const result = compressed && compressed.length < output.output.length
|
|
156
163
|
? compressed
|
|
157
164
|
: truncate(output.output);
|
|
158
165
|
const originalSize = output.output.length;
|
|
159
166
|
output.output = `${result}\n[rtk: compressed method=${result === compressed ? `rtk/${selected}` : "head+tail"}; sizes=${originalSize}->${result.length}; evidence_complete=false; loss=possible]`;
|
|
167
|
+
input.result = replaceResultText(input.result, output.output);
|
|
160
168
|
if (statsPath) {
|
|
161
169
|
const method = result === compressed
|
|
162
170
|
? "compressed-rtk"
|
|
@@ -174,4 +182,11 @@ export async function rtk(options = {}) {
|
|
|
174
182
|
},
|
|
175
183
|
};
|
|
176
184
|
}
|
|
177
|
-
export default
|
|
185
|
+
export default {
|
|
186
|
+
id: "agentomatic.rtk",
|
|
187
|
+
async setup(ctx) {
|
|
188
|
+
const hooks = await rtk(ctx.options);
|
|
189
|
+
if (hooks["execute.after"])
|
|
190
|
+
await ctx.tool.hook("execute.after", hooks["execute.after"]);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
@@ -1,22 +1,32 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
2
|
+
import { type ToolAfter } from "./events.js";
|
|
1
3
|
export type RulesInjectorOptions = {
|
|
2
4
|
budget?: number;
|
|
3
5
|
cwd?: string;
|
|
4
6
|
enabled?: boolean;
|
|
5
7
|
};
|
|
6
8
|
export declare function rulesInjector(options?: RulesInjectorOptions): Promise<{
|
|
7
|
-
"
|
|
9
|
+
"execute.after"?: undefined;
|
|
8
10
|
event?: undefined;
|
|
9
|
-
|
|
11
|
+
context?: undefined;
|
|
10
12
|
} | {
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
event?: {
|
|
14
|
-
type?: string;
|
|
15
|
-
properties?: Record<string, unknown>;
|
|
16
|
-
};
|
|
13
|
+
"execute.after": (input: ToolAfter & {
|
|
14
|
+
directory?: string;
|
|
17
15
|
}) => Promise<void>;
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
event: (event: {
|
|
17
|
+
type: string;
|
|
18
|
+
data?: unknown;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
context: (input: {
|
|
21
|
+
sessionID: string;
|
|
22
|
+
system: Array<{
|
|
23
|
+
type: "text";
|
|
24
|
+
text: string;
|
|
25
|
+
}>;
|
|
20
26
|
}) => Promise<void>;
|
|
21
27
|
}>;
|
|
22
|
-
|
|
28
|
+
declare const _default: {
|
|
29
|
+
id: string;
|
|
30
|
+
setup(ctx: Plugin.Context): Promise<(() => Promise<void>) | undefined>;
|
|
31
|
+
};
|
|
32
|
+
export default _default;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resultText, replaceResultText, subscribeEvents, toolInput, } from "./events.js";
|
|
1
2
|
import { lstat, readFile, realpath } from "node:fs/promises";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
4
|
import { dirname, resolve } from "node:path";
|
|
@@ -9,20 +10,6 @@ function sessionID(value) {
|
|
|
9
10
|
return item[key];
|
|
10
11
|
return undefined;
|
|
11
12
|
}
|
|
12
|
-
function pathOf(input, output) {
|
|
13
|
-
for (const value of [input, output]) {
|
|
14
|
-
const args = value?.args;
|
|
15
|
-
for (const key of ["filePath", "file_path", "path"])
|
|
16
|
-
if (typeof args?.[key] === "string")
|
|
17
|
-
return args[key];
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
function output(value, text) {
|
|
22
|
-
const item = value;
|
|
23
|
-
if (typeof item?.output === "string")
|
|
24
|
-
item.output = `${item.output}\n${text}`;
|
|
25
|
-
}
|
|
26
13
|
function marker(rule) {
|
|
27
14
|
return `[rules-injector source=${rule.path} revision=${rule.revision}]`;
|
|
28
15
|
}
|
|
@@ -44,16 +31,19 @@ export async function rulesInjector(options = {}) {
|
|
|
44
31
|
}
|
|
45
32
|
return item;
|
|
46
33
|
};
|
|
47
|
-
const inject = async (input
|
|
48
|
-
|
|
49
|
-
if (call?.tool !== "read" && call?.tool !== "edit")
|
|
34
|
+
const inject = async (input) => {
|
|
35
|
+
if (input.status !== "completed" || (input.tool !== "read" && input.tool !== "edit"))
|
|
50
36
|
return;
|
|
51
|
-
const identifier = sessionID
|
|
52
|
-
const target =
|
|
53
|
-
|
|
37
|
+
const identifier = input.sessionID;
|
|
38
|
+
const target = toolInput(input.input).path;
|
|
39
|
+
const original = resultText(input.result);
|
|
40
|
+
if (!identifier || typeof target !== "string" || original === undefined)
|
|
54
41
|
return;
|
|
42
|
+
const output = (text) => {
|
|
43
|
+
input.result = replaceResultText(input.result, `${original}\n${text}`);
|
|
44
|
+
};
|
|
55
45
|
try {
|
|
56
|
-
const native =
|
|
46
|
+
const native = input.directory ?? cwd;
|
|
57
47
|
const paths = [];
|
|
58
48
|
let current = dirname(resolve(native, target));
|
|
59
49
|
while (true) {
|
|
@@ -103,29 +93,51 @@ export async function rulesInjector(options = {}) {
|
|
|
103
93
|
}
|
|
104
94
|
}
|
|
105
95
|
if (additions.length)
|
|
106
|
-
output(
|
|
96
|
+
output(additions.join("\n\n"));
|
|
107
97
|
}
|
|
108
98
|
catch (error) {
|
|
109
|
-
output(
|
|
99
|
+
output(`[rules-injector warning] cannot read AGENTS.md: ${String(error)}`);
|
|
110
100
|
}
|
|
111
101
|
};
|
|
112
102
|
return {
|
|
113
|
-
"
|
|
114
|
-
event: async (
|
|
115
|
-
if (event
|
|
116
|
-
const identifier = sessionID(event.
|
|
103
|
+
"execute.after": inject,
|
|
104
|
+
event: async (event) => {
|
|
105
|
+
if (event.type === "session.compacted" || event.type.includes("compaction")) {
|
|
106
|
+
const identifier = sessionID(event.data);
|
|
117
107
|
if (identifier)
|
|
118
108
|
state(identifier).replay = true;
|
|
119
109
|
}
|
|
120
110
|
},
|
|
121
|
-
|
|
111
|
+
context: async (input) => {
|
|
122
112
|
const identifier = sessionID(input);
|
|
123
|
-
if (!identifier || !state(identifier).replay
|
|
113
|
+
if (!identifier || !state(identifier).replay)
|
|
124
114
|
return;
|
|
125
115
|
for (const rule of state(identifier).loaded.values())
|
|
126
|
-
|
|
116
|
+
input.system.push({ type: "text", text: block(rule) });
|
|
127
117
|
state(identifier).replay = false;
|
|
128
118
|
},
|
|
129
119
|
};
|
|
130
120
|
}
|
|
131
|
-
export default
|
|
121
|
+
export default {
|
|
122
|
+
id: "agentomatic.rules-injector",
|
|
123
|
+
async setup(ctx) {
|
|
124
|
+
const hooks = await rulesInjector({ ...ctx.options, cwd: ctx.location.directory });
|
|
125
|
+
if (hooks["execute.after"])
|
|
126
|
+
await ctx.tool.hook("execute.after", async (event) => {
|
|
127
|
+
if (event.status !== "completed" || (event.tool !== "read" && event.tool !== "edit"))
|
|
128
|
+
return;
|
|
129
|
+
const session = await ctx.session
|
|
130
|
+
.get({ sessionID: event.sessionID })
|
|
131
|
+
.catch(() => undefined);
|
|
132
|
+
if (session) {
|
|
133
|
+
const scoped = { ...event, directory: session.location.directory };
|
|
134
|
+
await hooks["execute.after"](scoped);
|
|
135
|
+
event.result = scoped.result;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
if (hooks.context)
|
|
139
|
+
await ctx.session.hook("context", hooks.context);
|
|
140
|
+
if (hooks.event)
|
|
141
|
+
return subscribeEvents(ctx, hooks.event);
|
|
142
|
+
},
|
|
143
|
+
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
1
2
|
export type ZedBellOptions = {
|
|
2
3
|
enabled?: boolean;
|
|
3
4
|
};
|
|
4
5
|
export declare function zedBell(options?: ZedBellOptions): Promise<{
|
|
5
6
|
event?: undefined;
|
|
6
7
|
} | {
|
|
7
|
-
event: (
|
|
8
|
-
|
|
9
|
-
type?: string;
|
|
10
|
-
};
|
|
8
|
+
event: (event: {
|
|
9
|
+
type: string;
|
|
11
10
|
}) => Promise<void>;
|
|
12
11
|
}>;
|
|
13
|
-
|
|
12
|
+
declare const _default: {
|
|
13
|
+
id: string;
|
|
14
|
+
setup(ctx: Plugin.Context): Promise<(() => Promise<void>) | undefined>;
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
package/dist/plugins/zed-bell.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { subscribeEvents } from "./events.js";
|
|
1
2
|
export async function zedBell(options = {}) {
|
|
2
3
|
if (!options.enabled)
|
|
3
4
|
return {};
|
|
4
5
|
return {
|
|
5
|
-
event: async (
|
|
6
|
+
event: async (event) => {
|
|
6
7
|
if (event.type !== "session.idle" && event.type !== "permission.asked")
|
|
7
8
|
return;
|
|
8
9
|
const acp = process.env.OPENCODE_CLIENT === "acp" || process.argv.includes("acp");
|
|
@@ -11,4 +12,11 @@ export async function zedBell(options = {}) {
|
|
|
11
12
|
},
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
|
-
export default
|
|
15
|
+
export default {
|
|
16
|
+
id: "agentomatic.zed-bell",
|
|
17
|
+
async setup(ctx) {
|
|
18
|
+
const hooks = await zedBell(ctx.options);
|
|
19
|
+
if (hooks.event)
|
|
20
|
+
return subscribeEvents(ctx, hooks.event);
|
|
21
|
+
},
|
|
22
|
+
};
|
package/dist/reconcile.d.ts
CHANGED
|
@@ -27,13 +27,16 @@ export type ReconcilePlan = {
|
|
|
27
27
|
sha256?: string;
|
|
28
28
|
}>;
|
|
29
29
|
confirmable: boolean;
|
|
30
|
+
digest: string;
|
|
30
31
|
};
|
|
31
32
|
export type ReconcileResult = {
|
|
32
33
|
status: "ok";
|
|
33
34
|
applied: true;
|
|
34
35
|
plan: ReconcilePlan;
|
|
35
36
|
};
|
|
36
|
-
export type ReconcileOptions = TransactionOptions
|
|
37
|
+
export type ReconcileOptions = TransactionOptions & {
|
|
38
|
+
expectedDigest?: string;
|
|
39
|
+
};
|
|
37
40
|
export declare class ReconcileError extends LifecycleError {
|
|
38
41
|
}
|
|
39
42
|
export declare function previewReconcile(scope: Scope, cwd?: string, home?: string): Promise<ReconcilePlan>;
|
package/dist/reconcile.js
CHANGED
|
@@ -3,7 +3,7 @@ import { lstat, readdir, readFile } from "node:fs/promises";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { applyTransaction,
|
|
6
|
+
import { applyTransaction, assertSafePath, destination, LifecycleError, lifecycleRoot, readRegular, sha256, stable, withLifecycleLock, } from "./lifecycle.js";
|
|
7
7
|
import { archiveMutations } from "./installer.js";
|
|
8
8
|
const PACKAGE_NAME = "@kisev/agentomatic";
|
|
9
9
|
const GENERIC_MANIFEST = ".agentomatic-manifest.json";
|
|
@@ -35,9 +35,6 @@ function loadInventory() {
|
|
|
35
35
|
function scopeRoot(scope, cwd, home) {
|
|
36
36
|
return scope === "global" ? resolve(home) : resolve(cwd);
|
|
37
37
|
}
|
|
38
|
-
function reconcileAllowedRoots(scope, cwd, home) {
|
|
39
|
-
return [archiveRoot(scope, cwd, home)];
|
|
40
|
-
}
|
|
41
38
|
function relativePath(root, target) {
|
|
42
39
|
const value = relative(resolve(root), resolve(target));
|
|
43
40
|
if (!value || value.startsWith(`..${sep}`) || value === "..")
|
|
@@ -398,22 +395,19 @@ async function build(scope, cwd = process.cwd(), home = homedir()) {
|
|
|
398
395
|
groups["modified-managed"].length === 0 &&
|
|
399
396
|
groups.conflict.length === 0;
|
|
400
397
|
return {
|
|
401
|
-
plan: { ...base, confirmable },
|
|
398
|
+
plan: { ...base, confirmable, digest: sha256(stable({ base, mutations })) },
|
|
402
399
|
mutations,
|
|
403
400
|
};
|
|
404
401
|
}
|
|
405
402
|
export async function previewReconcile(scope, cwd = process.cwd(), home = homedir()) {
|
|
406
403
|
const stateRoot = lifecycleRoot(scope, cwd, home);
|
|
407
|
-
const root = scopeRoot(scope, cwd, home);
|
|
408
404
|
try {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
return { ...built.plan, confirmable: !blocked && actionable };
|
|
416
|
-
});
|
|
405
|
+
if (await readRegular(join(stateRoot, "transaction-journal.json")))
|
|
406
|
+
throw new ReconcileError("recovery_required", "Run maintenance recover before a fresh preview");
|
|
407
|
+
const built = await build(scope, cwd, home);
|
|
408
|
+
const blocked = built.plan.modified_managed.length > 0 || built.plan.conflicts.length > 0;
|
|
409
|
+
const actionable = built.plan.operations.length > 0;
|
|
410
|
+
return { ...built.plan, confirmable: !blocked && actionable };
|
|
417
411
|
}
|
|
418
412
|
catch (error) {
|
|
419
413
|
if (error instanceof ReconcileError)
|
|
@@ -432,9 +426,11 @@ export async function applyReconcile(scope, cwd = process.cwd(), home = homedir(
|
|
|
432
426
|
const root = scopeRoot(scope, cwd, home);
|
|
433
427
|
try {
|
|
434
428
|
return await withLifecycleLock(stateRoot, async () => {
|
|
435
|
-
if (await
|
|
436
|
-
throw new ReconcileError("
|
|
429
|
+
if (await readRegular(join(stateRoot, "transaction-journal.json")))
|
|
430
|
+
throw new ReconcileError("recovery_required", "Run maintenance recover before a fresh preview");
|
|
437
431
|
const built = await build(scope, cwd, home);
|
|
432
|
+
if (options.expectedDigest && options.expectedDigest !== built.plan.digest)
|
|
433
|
+
throw new ReconcileError("stale_plan", "Cleanup sources changed; request a fresh preview");
|
|
438
434
|
if (built.plan.conflicts.length || built.plan.modified_managed.length)
|
|
439
435
|
throw new ReconcileError("conflict", "Reconcile contains unsafe ownership conflicts");
|
|
440
436
|
await applyTransaction(root, stateRoot, built.mutations, {
|
package/dist/registry.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type PackageCommandRegistration = {
|
|
|
9
9
|
body: readonly string[];
|
|
10
10
|
};
|
|
11
11
|
export type CommandRegistration = SkillCommandRegistration | PackageCommandRegistration;
|
|
12
|
+
export declare const COMMANDLESS_SKILLS: readonly ["debugging", "tdd", "verification"];
|
|
12
13
|
export declare const COMMAND_REGISTRY: ({
|
|
13
14
|
name: string;
|
|
14
15
|
description: string;
|
package/dist/registry.js
CHANGED
|
@@ -4,13 +4,16 @@ function description(english, russianTrigger) {
|
|
|
4
4
|
}
|
|
5
5
|
const SKILL_NAMES = [
|
|
6
6
|
"agents-md",
|
|
7
|
+
"asd-ste100",
|
|
7
8
|
"askme",
|
|
8
9
|
"ast-grep",
|
|
9
10
|
"code-explain",
|
|
10
11
|
"code-review",
|
|
12
|
+
"code-simplify",
|
|
11
13
|
"commit-msg",
|
|
12
14
|
"docs-prepare",
|
|
13
15
|
"docs-review",
|
|
16
|
+
"eli5",
|
|
14
17
|
"goal",
|
|
15
18
|
"humanize",
|
|
16
19
|
"mattermost",
|
|
@@ -19,10 +22,10 @@ const SKILL_NAMES = [
|
|
|
19
22
|
"release-prepare",
|
|
20
23
|
"release-review",
|
|
21
24
|
"rtk",
|
|
22
|
-
"skill-
|
|
25
|
+
"skill-doctor",
|
|
23
26
|
"slides-prompts-prepare",
|
|
24
27
|
"spec-manage",
|
|
25
|
-
"
|
|
28
|
+
"handoff",
|
|
26
29
|
"briefing",
|
|
27
30
|
"task-prepare",
|
|
28
31
|
"task-review",
|
|
@@ -42,13 +45,21 @@ const SKILL_NAMES = [
|
|
|
42
45
|
"team-sprint-close",
|
|
43
46
|
"team-sprint-start",
|
|
44
47
|
];
|
|
48
|
+
// Skills that opt out of command ownership through their frontmatter
|
|
49
|
+
// `metadata.command: false` label (the inspired-by era). They are installed
|
|
50
|
+
// like every other skill and load through the native Skill tool; only the
|
|
51
|
+
// thin slash-command adapter is absent. Keep this list in sync with the
|
|
52
|
+
// frontmatter labels; tests pin the two-way mapping.
|
|
53
|
+
export const COMMANDLESS_SKILLS = ["debugging", "tdd", "verification"];
|
|
45
54
|
const COMMANDS = [
|
|
46
55
|
...SKILL_NAMES.map((name) => ({
|
|
47
56
|
name,
|
|
48
57
|
skill: name,
|
|
49
58
|
description: name === "spec-manage"
|
|
50
59
|
? description("Create greenfield specs, onboard an existing project, update target state, or review specs", "спецификация проекта")
|
|
51
|
-
:
|
|
60
|
+
: name === "code-simplify"
|
|
61
|
+
? description("Apply the passive prevention ladder while coding and audit a requested scope for unnecessary complexity on an explicit request", "упростить код")
|
|
62
|
+
: description(`Run the ${name} Agent Skill`, name),
|
|
52
63
|
})),
|
|
53
64
|
{
|
|
54
65
|
name: "rtk-stats",
|
|
@@ -57,7 +68,7 @@ const COMMANDS = [
|
|
|
57
68
|
"Show the RTK output-compression observability summary for this host.",
|
|
58
69
|
`Run \`npx --yes @kisev/agentomatic@${requirePackageVersion()} doctor --json\` and render the \`rtk.observability\` check as a short human summary: wrapper status, rtk binary availability, event counters, characters saved, and the token estimate.`,
|
|
59
70
|
"When the doctor command is unavailable, read the stats file directly: `$XDG_STATE_HOME/opencode/skills/rtk/stats.json`, or `~/.local/state/opencode/skills/rtk/stats.json` when that variable is unset.",
|
|
60
|
-
"Zero counters with an active wrapper mean no verbose
|
|
71
|
+
"Zero counters with an active wrapper mean no verbose shell output has been compressed yet.",
|
|
61
72
|
"The `/rtk` command still loads the portable rtk skill and is unaffected by this summary.",
|
|
62
73
|
],
|
|
63
74
|
},
|
|
@@ -83,7 +94,12 @@ export function renderCommand(command) {
|
|
|
83
94
|
"Natural requests are supported. Explicit mode and scope arguments are passed unchanged; the skill verifies safety preconditions and asks before writing if intent remains ambiguous.",
|
|
84
95
|
"Examples: `Create canonical specs for this empty project`; `Document this existing service`; `Change the canonical timeout`; `Audit specs without changes`.",
|
|
85
96
|
]
|
|
86
|
-
:
|
|
97
|
+
: command.name === "code-simplify"
|
|
98
|
+
? [
|
|
99
|
+
"Prevention applies passively while coding: the ladder runs on the current change without scanning anything; a targeted caller search by exact name for the code being changed is not scanning, and a conscious cut records a SIMPLIFY debt marker in the same edit.",
|
|
100
|
+
"Audit is explicit and report-only: name the scope, receive ranked one-line tagged findings plus a separate debt-marker registry, and change nothing.",
|
|
101
|
+
]
|
|
102
|
+
: [];
|
|
87
103
|
return [
|
|
88
104
|
"---",
|
|
89
105
|
`description: ${command.description}`,
|
package/dist/routing.d.ts
CHANGED
|
@@ -123,11 +123,13 @@ export declare class ExecutionCardLifecycle {
|
|
|
123
123
|
}
|
|
124
124
|
export declare class RoutingGate {
|
|
125
125
|
#private;
|
|
126
|
+
requiresReceipt(sessionID: string, agent: string | undefined): boolean;
|
|
127
|
+
hasActive(sessionID: string, agent: string, callID?: string): boolean;
|
|
126
128
|
preview(input: RoutingInput): RoutingDecision;
|
|
127
129
|
dispatch(input: RoutingInput, provided: unknown): RoutingDecision;
|
|
128
130
|
grant(sessionID: string, decision: RoutingDecision, context?: ReceiptContext): RoutingReceipt;
|
|
129
|
-
cancel(sessionID: string): void;
|
|
130
|
-
consume(sessionID: string, agent: string, context?: ReceiptContext): void;
|
|
131
|
-
complete(sessionID: string, agent: string, output: unknown): void;
|
|
131
|
+
cancel(sessionID: string, callID?: string): void;
|
|
132
|
+
consume(sessionID: string, agent: string, context?: ReceiptContext, callID?: string): void;
|
|
133
|
+
complete(sessionID: string, agent: string, output: unknown, callID?: string): void;
|
|
132
134
|
}
|
|
133
135
|
export {};
|