@agent-workshop/adoc-core 0.1.0

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.
Files changed (64) hide show
  1. package/LICENSE +5 -0
  2. package/THIRD_PARTY_NOTICES.md +49 -0
  3. package/dist/claim.d.ts +17 -0
  4. package/dist/claim.js +35 -0
  5. package/dist/claim.js.map +1 -0
  6. package/dist/client.d.ts +14 -0
  7. package/dist/client.js +49 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/config.d.ts +39 -0
  10. package/dist/config.js +110 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/errors.d.ts +6 -0
  13. package/dist/errors.js +13 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/fetch.d.ts +19 -0
  16. package/dist/fetch.js +106 -0
  17. package/dist/fetch.js.map +1 -0
  18. package/dist/git.d.ts +1 -0
  19. package/dist/git.js +13 -0
  20. package/dist/git.js.map +1 -0
  21. package/dist/herdr.d.ts +44 -0
  22. package/dist/herdr.js +134 -0
  23. package/dist/herdr.js.map +1 -0
  24. package/dist/home.d.ts +12 -0
  25. package/dist/home.js +43 -0
  26. package/dist/home.js.map +1 -0
  27. package/dist/index.d.ts +18 -0
  28. package/dist/index.js +19 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/messages.d.ts +76 -0
  31. package/dist/messages.js +119 -0
  32. package/dist/messages.js.map +1 -0
  33. package/dist/names.d.ts +14 -0
  34. package/dist/names.js +19 -0
  35. package/dist/names.js.map +1 -0
  36. package/dist/plugins.d.ts +43 -0
  37. package/dist/plugins.js +157 -0
  38. package/dist/plugins.js.map +1 -0
  39. package/dist/registry.d.ts +11 -0
  40. package/dist/registry.js +39 -0
  41. package/dist/registry.js.map +1 -0
  42. package/dist/report.d.ts +3 -0
  43. package/dist/report.js +9 -0
  44. package/dist/report.js.map +1 -0
  45. package/dist/scan.d.ts +35 -0
  46. package/dist/scan.js +163 -0
  47. package/dist/scan.js.map +1 -0
  48. package/dist/server.d.ts +80 -0
  49. package/dist/server.js +576 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/skills.d.ts +46 -0
  52. package/dist/skills.js +131 -0
  53. package/dist/skills.js.map +1 -0
  54. package/dist/terminal.d.ts +28 -0
  55. package/dist/terminal.js +110 -0
  56. package/dist/terminal.js.map +1 -0
  57. package/dist/transports.d.ts +21 -0
  58. package/dist/transports.js +69 -0
  59. package/dist/transports.js.map +1 -0
  60. package/dist/workspace.d.ts +182 -0
  61. package/dist/workspace.js +392 -0
  62. package/dist/workspace.js.map +1 -0
  63. package/package.json +54 -0
  64. package/skill/SKILL.md +219 -0
@@ -0,0 +1,46 @@
1
+ import { type SkillSource } from './plugins.js';
2
+ import type { CheckEntry, Workspace } from './workspace.js';
3
+ /** One _Agent_Skill_ of the workspace; `pluginKey` for a _Plugin_Skill_. */
4
+ export interface SkillEntry extends SkillSource {
5
+ readonly pluginKey?: string;
6
+ }
7
+ /** Every _Agent_Skill_: the core package and the kit in user scope, then each loaded plugin's skill in its scope. */
8
+ export declare function listSkills(workspace: Workspace): Promise<SkillEntry[]>;
9
+ /** The `SKILL.md` of a skill, selected by its name as `adoc skill list` shows it, without its front matter. */
10
+ export declare function viewSkill(workspace: Workspace, name: string): Promise<{
11
+ entry: SkillEntry;
12
+ body: string;
13
+ }>;
14
+ export interface SkillInstallOptions {
15
+ /** Agent names passed on to `skills --agent`, such as `claude-code` or `codex`; the skills CLI picks them when empty. */
16
+ agents?: string[];
17
+ env?: NodeJS.ProcessEnv;
18
+ }
19
+ /** _Skill_Install_Command_ `install` / `update`: `skills add <folder>` for each skill, `-g` for user scope. */
20
+ export declare function installSkills(workspace: Workspace, options?: SkillInstallOptions): Promise<SkillEntry[]>;
21
+ /** _Skill_Install_Command_ `uninstall`: `skills remove` for the skills of each scope. */
22
+ export declare function uninstallSkills(workspace: Workspace, options?: SkillInstallOptions): Promise<SkillEntry[]>;
23
+ /**
24
+ * _Skill_Check_: each skill must be installed in `.agents/skills` or `.claude/skills` of the workspace or of the user's
25
+ * home, with the same `SKILL.md` as its source.
26
+ */
27
+ export declare function checkSkills(workspace: Workspace, home?: string): Promise<CheckEntry[]>;
28
+ /** The `SKILL.md` of a skill with its version, for the _File_Editor_. */
29
+ export declare function readSkillFile(workspace: Workspace, name: string): Promise<{
30
+ name: string;
31
+ file: string;
32
+ version: string;
33
+ text: string;
34
+ }>;
35
+ /**
36
+ * Writes a `SKILL.md` that the user edited in the _File_Editor_, when `version` is still current; answers with the
37
+ * new version and the unified diff from `since` (the text the unsent edits started from) to the new text.
38
+ */
39
+ export declare function editSkillFile(workspace: Workspace, name: string, version: string, text: string, since?: string): Promise<{
40
+ status: 'applied';
41
+ version: string;
42
+ diff: string;
43
+ } | {
44
+ status: 'refused';
45
+ reason: string;
46
+ }>;
package/dist/skills.js ADDED
@@ -0,0 +1,131 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { createRequire } from 'node:module';
5
+ import { homedir } from 'node:os';
6
+ import { dirname, isAbsolute, join, relative } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { unifiedDiff } from '@agent-workshop/adoc-plugin-kit';
9
+ import { AdocError } from './errors.js';
10
+ import { readSkillSource } from './plugins.js';
11
+ /** The folders of the core package and the _Plugin_Kit_, which own the skills `adoc` and `adoc-plugin-authoring`. */
12
+ const OWNERS = [dirname(dirname(fileURLToPath(import.meta.url))), dirname(dirname(createRequire(import.meta.url).resolve('@agent-workshop/adoc-plugin-kit/skill/SKILL.md')))];
13
+ /** Every _Agent_Skill_: the core package and the kit in user scope, then each loaded plugin's skill in its scope. */
14
+ export async function listSkills(workspace) {
15
+ const entries = [];
16
+ for (const owner of OWNERS)
17
+ entries.push(await readSkillSource(owner, 'user'));
18
+ for (const plugin of workspace.loadedPlugins())
19
+ if (plugin.skill)
20
+ entries.push({ ...plugin.skill, pluginKey: plugin.key });
21
+ return entries;
22
+ }
23
+ /** The `SKILL.md` of a skill, selected by its name as `adoc skill list` shows it, without its front matter. */
24
+ export async function viewSkill(workspace, name) {
25
+ const entries = await listSkills(workspace);
26
+ const entry = entries.find((e) => e.name === name);
27
+ if (!entry)
28
+ throw new AdocError('skill.missing', `No skill ${name}. Known: ${entries.map((e) => e.name).join(', ')}.`);
29
+ const text = await readFile(join(entry.directory, 'SKILL.md'), 'utf8');
30
+ return { entry, body: text.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n+/, '') };
31
+ }
32
+ /** How to run the Vercel `skills` CLI: `ADOC_SKILLS_CLI`, by default `npx -y skills@1`. */
33
+ function skillsCommand(env) {
34
+ return (env.ADOC_SKILLS_CLI || 'npx -y skills@1').split(/\s+/).filter(Boolean);
35
+ }
36
+ function runSkills(args, cwd, env) {
37
+ const [command, ...prefix] = skillsCommand(env);
38
+ return new Promise((done, fail) => {
39
+ const child = spawn(command, [...prefix, ...args], { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
40
+ let output = '';
41
+ child.stdout.on('data', (chunk) => (output += chunk.toString('utf8')));
42
+ child.stderr.on('data', (chunk) => (output += chunk.toString('utf8')));
43
+ child.on('error', (error) => fail(new AdocError('skill.install', `cannot run ${command}: ${error.message}`)));
44
+ child.on('exit', (code) => (code === 0 ? done(output) : fail(new AdocError('skill.install', `${[command, ...prefix, ...args].join(' ')} failed (exit ${code}):\n${output.trim()}`))));
45
+ });
46
+ }
47
+ /** _Skill_Install_Command_ `install` / `update`: `skills add <folder>` for each skill, `-g` for user scope. */
48
+ export async function installSkills(workspace, options = {}) {
49
+ const env = options.env ?? process.env;
50
+ const entries = await listSkills(workspace);
51
+ const agents = options.agents?.length ? ['--agent', ...options.agents] : [];
52
+ for (const entry of entries)
53
+ await runSkills(['add', entry.directory, '--yes', ...(entry.scope === 'user' ? ['--global'] : []), ...agents], workspace.root, env);
54
+ return entries;
55
+ }
56
+ /** _Skill_Install_Command_ `uninstall`: `skills remove` for the skills of each scope. */
57
+ export async function uninstallSkills(workspace, options = {}) {
58
+ const env = options.env ?? process.env;
59
+ const entries = await listSkills(workspace);
60
+ const agents = options.agents?.length ? ['--agent', ...options.agents] : [];
61
+ for (const scope of ['project', 'user']) {
62
+ const names = entries.filter((e) => e.scope === scope).map((e) => e.name);
63
+ if (names.length)
64
+ await runSkills(['remove', ...names, '--yes', ...(scope === 'user' ? ['--global'] : []), ...agents], workspace.root, env);
65
+ }
66
+ return entries;
67
+ }
68
+ async function readIfThere(path) {
69
+ try {
70
+ return await readFile(path, 'utf8');
71
+ }
72
+ catch {
73
+ return undefined;
74
+ }
75
+ }
76
+ /**
77
+ * _Skill_Check_: each skill must be installed in `.agents/skills` or `.claude/skills` of the workspace or of the user's
78
+ * home, with the same `SKILL.md` as its source.
79
+ */
80
+ export async function checkSkills(workspace, home = homedir()) {
81
+ const found = [];
82
+ for (const entry of await listSkills(workspace)) {
83
+ const source = await readFile(join(entry.directory, 'SKILL.md'), 'utf8');
84
+ const installed = [];
85
+ for (const base of [workspace.root, home]) {
86
+ for (const place of ['.agents/skills', '.claude/skills']) {
87
+ const text = await readIfThere(join(base, place, entry.name, 'SKILL.md'));
88
+ if (text !== undefined)
89
+ installed.push(text);
90
+ }
91
+ }
92
+ if (!installed.length)
93
+ found.push({ level: 'warning', kind: 'skill-missing', message: `The agent skill ${entry.name} is not installed; run adoc skill install.` });
94
+ else if (!installed.includes(source))
95
+ found.push({ level: 'warning', kind: 'skill-outdated', message: `The installed agent skill ${entry.name} differs from this adoc; run adoc skill update.` });
96
+ }
97
+ return found;
98
+ }
99
+ /** The version of a skill file: a hash of its text, as for documents. */
100
+ function skillVersion(text) {
101
+ return createHash('sha256').update(text).digest('hex').slice(0, 16);
102
+ }
103
+ /** The path of a skill file as people read it: relative to the workspace when inside it, otherwise from the home. */
104
+ function shownPath(workspace, path) {
105
+ const rel = relative(workspace.root, path);
106
+ if (!rel.startsWith('..') && !isAbsolute(rel))
107
+ return rel;
108
+ const home = homedir();
109
+ return path.startsWith(`${home}/`) ? `~/${path.slice(home.length + 1)}` : path;
110
+ }
111
+ /** The `SKILL.md` of a skill with its version, for the _File_Editor_. */
112
+ export async function readSkillFile(workspace, name) {
113
+ const entry = (await listSkills(workspace)).find((e) => e.name === name);
114
+ if (!entry)
115
+ throw new AdocError('skill.missing', `No skill ${name}.`);
116
+ const text = await readFile(join(entry.directory, 'SKILL.md'), 'utf8');
117
+ return { name, file: shownPath(workspace, join(entry.directory, 'SKILL.md')), version: skillVersion(text), text };
118
+ }
119
+ /**
120
+ * Writes a `SKILL.md` that the user edited in the _File_Editor_, when `version` is still current; answers with the
121
+ * new version and the unified diff from `since` (the text the unsent edits started from) to the new text.
122
+ */
123
+ export async function editSkillFile(workspace, name, version, text, since) {
124
+ const current = await readSkillFile(workspace, name);
125
+ if (current.version !== version)
126
+ return { status: 'refused', reason: `${current.file} changed while you were editing; your text is kept, copy it and edit again.` };
127
+ const entry = (await listSkills(workspace)).find((e) => e.name === name);
128
+ await writeFile(join(entry.directory, 'SKILL.md'), text);
129
+ return { status: 'applied', version: skillVersion(text), diff: unifiedDiff(since ?? current.text, text, current.file) };
130
+ }
131
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { WebSocket } from 'ws';
2
+ import type { AgentClaim } from './claim.js';
3
+ type Mode = 'observe' | 'control';
4
+ /**
5
+ * _Agent_Terminal_Stream_: relays the claimed pane's terminal between herdr and browser sockets.
6
+ * One socket at a time controls the terminal (`herdr terminal session control`); every other socket observes.
7
+ */
8
+ export declare class TerminalRelay {
9
+ private readonly claim;
10
+ private readonly log;
11
+ private readonly streams;
12
+ constructor(claim: () => AgentClaim | undefined, log: (line: string) => void);
13
+ /** Starts or restarts the stream of a socket in the given mode and size; taking control downgrades the previous controller. */
14
+ open(ws: WebSocket, mode: Mode, cols: number, rows: number): void;
15
+ /** Sends keys or text typed in the controlling browser. */
16
+ input(ws: WebSocket, data: string): void;
17
+ resize(ws: WebSocket, cols: number, rows: number): void;
18
+ /** Scrolls the pane's view through its scrollback: positive lines scroll up (back in history), negative down. */
19
+ scroll(ws: WebSocket, lines: number): void;
20
+ /** Sends a mouse event at a 0-based cell; herdr passes it to the program when the program tracks the mouse. */
21
+ mouse(ws: WebSocket, action: 'down' | 'up' | 'drag' | 'move', button: 'left' | 'middle' | 'right', column: number, row: number): void;
22
+ /** Stops the stream of a socket; a controller releases the pane first so that it gets its own size back. */
23
+ stop(ws: WebSocket): void;
24
+ /** Ends every stream, for example when the claim changes; browsers reopen with the new pane. */
25
+ stopAll(reason: string): void;
26
+ private command;
27
+ }
28
+ export {};
@@ -0,0 +1,110 @@
1
+ import { spawn } from 'node:child_process';
2
+ /**
3
+ * _Agent_Terminal_Stream_: relays the claimed pane's terminal between herdr and browser sockets.
4
+ * One socket at a time controls the terminal (`herdr terminal session control`); every other socket observes.
5
+ */
6
+ export class TerminalRelay {
7
+ claim;
8
+ log;
9
+ streams = new Map();
10
+ constructor(claim, log) {
11
+ this.claim = claim;
12
+ this.log = log;
13
+ }
14
+ /** Starts or restarts the stream of a socket in the given mode and size; taking control downgrades the previous controller. */
15
+ open(ws, mode, cols, rows) {
16
+ const claim = this.claim();
17
+ if (!claim) {
18
+ send(ws, { type: 'terminal.closed', reason: 'no-claim' });
19
+ return;
20
+ }
21
+ if (mode === 'control') {
22
+ for (const [other, stream] of this.streams)
23
+ if (other !== ws && stream.mode === 'control')
24
+ this.open(other, 'observe', stream.cols, stream.rows);
25
+ }
26
+ this.stop(ws);
27
+ const args = ['terminal', 'session', mode, claim.pane, '--cols', String(cols), '--rows', String(rows)];
28
+ const child = spawn('herdr', args, { env: { ...process.env, HERDR_SOCKET_PATH: claim.socket } });
29
+ const stream = { mode, child, cols, rows };
30
+ this.streams.set(ws, stream);
31
+ let buffer = '';
32
+ child.stdout.on('data', (chunk) => {
33
+ buffer += chunk.toString('utf8');
34
+ let end;
35
+ while ((end = buffer.indexOf('\n')) >= 0) {
36
+ const line = buffer.slice(0, end);
37
+ buffer = buffer.slice(end + 1);
38
+ try {
39
+ const record = JSON.parse(line);
40
+ if (record.type === 'terminal.frame' || record.type === 'terminal.closed')
41
+ send(ws, record);
42
+ }
43
+ catch {
44
+ // Ignore a malformed line.
45
+ }
46
+ }
47
+ });
48
+ child.stderr.on('data', (chunk) => this.log(`adoc: herdr terminal ${mode}: ${chunk.toString('utf8').trim()}`));
49
+ child.on('exit', () => {
50
+ if (this.streams.get(ws)?.child === child) {
51
+ this.streams.delete(ws);
52
+ send(ws, { type: 'terminal.closed', reason: 'stream-ended' });
53
+ }
54
+ });
55
+ send(ws, { type: 'terminal.mode', mode });
56
+ }
57
+ /** Sends keys or text typed in the controlling browser. */
58
+ input(ws, data) {
59
+ this.command(ws, { type: 'terminal.input', text: data });
60
+ }
61
+ resize(ws, cols, rows) {
62
+ const stream = this.streams.get(ws);
63
+ if (!stream)
64
+ return;
65
+ stream.cols = cols;
66
+ stream.rows = rows;
67
+ if (stream.mode === 'control')
68
+ this.command(ws, { type: 'terminal.resize', cols, rows });
69
+ else
70
+ this.open(ws, 'observe', cols, rows);
71
+ }
72
+ /** Scrolls the pane's view through its scrollback: positive lines scroll up (back in history), negative down. */
73
+ scroll(ws, lines) {
74
+ if (lines === 0)
75
+ return;
76
+ this.command(ws, { type: 'terminal.scroll', direction: lines > 0 ? 'up' : 'down', lines: Math.min(Math.abs(Math.round(lines)), 200) });
77
+ }
78
+ /** Sends a mouse event at a 0-based cell; herdr passes it to the program when the program tracks the mouse. */
79
+ mouse(ws, action, button, column, row) {
80
+ this.command(ws, { type: 'terminal.mouse', action, button, column: Math.max(0, Math.floor(column)), row: Math.max(0, Math.floor(row)) });
81
+ }
82
+ /** Stops the stream of a socket; a controller releases the pane first so that it gets its own size back. */
83
+ stop(ws) {
84
+ const stream = this.streams.get(ws);
85
+ if (!stream)
86
+ return;
87
+ this.streams.delete(ws);
88
+ if (stream.mode === 'control' && stream.child.stdin.writable)
89
+ stream.child.stdin.write(JSON.stringify({ type: 'terminal.release' }) + '\n');
90
+ stream.child.stdin.end();
91
+ setTimeout(() => stream.child.kill(), 500).unref();
92
+ }
93
+ /** Ends every stream, for example when the claim changes; browsers reopen with the new pane. */
94
+ stopAll(reason) {
95
+ for (const ws of [...this.streams.keys()]) {
96
+ this.stop(ws);
97
+ send(ws, { type: 'terminal.closed', reason });
98
+ }
99
+ }
100
+ command(ws, command) {
101
+ const stream = this.streams.get(ws);
102
+ if (stream?.mode === 'control' && stream.child.stdin.writable)
103
+ stream.child.stdin.write(JSON.stringify(command) + '\n');
104
+ }
105
+ }
106
+ function send(ws, message) {
107
+ if (ws.readyState === ws.OPEN)
108
+ ws.send(JSON.stringify(message));
109
+ }
110
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { AgentClaim } from './claim.js';
2
+ import type { TransportConfig } from './config.js';
3
+ import { type MessageQueue, type UserMessage } from './messages.js';
4
+ /**
5
+ * _User_Message_Transport_: how a held message reaches the _Assigned_Agent_.
6
+ * A push transport delivers each message as soon as it is created; the wait transport leaves
7
+ * messages held until `adoc message wait` takes them.
8
+ */
9
+ export interface MessageTransport {
10
+ readonly kind: string;
11
+ /** Pushes one message; resolves when delivered, rejects when delivery failed. Absent for the wait transport. */
12
+ push?(message: UserMessage): Promise<void>;
13
+ }
14
+ /** The current _Agent_Claim_, read when a message is pushed. */
15
+ export type ClaimSource = () => AgentClaim | undefined;
16
+ export declare function createTransport(config: TransportConfig, claim?: ClaimSource): MessageTransport;
17
+ /**
18
+ * Connects a push transport to the queue: every new message, and every message still held after a failure, is retried in order.
19
+ * Returns a function that retries the held messages, for example after a new _Agent_Claim_.
20
+ */
21
+ export declare function attachTransport(queue: MessageQueue, transport: MessageTransport, log: (line: string) => void): () => void;
@@ -0,0 +1,69 @@
1
+ import { herdrCall } from './herdr.js';
2
+ import { errorMessage } from './errors.js';
3
+ import { formatMessage } from './messages.js';
4
+ export function createTransport(config, claim = () => undefined) {
5
+ switch (config.kind) {
6
+ case 'wait':
7
+ return { kind: 'wait' };
8
+ case 'herdr':
9
+ return {
10
+ kind: 'herdr',
11
+ push: async (message) => {
12
+ const current = claim();
13
+ if (!current)
14
+ throw new Error('no agent has claimed this workspace; run adoc agent claim in the agent pane');
15
+ const text = formatMessage(message);
16
+ try {
17
+ await herdrCall(current.socket, 'agent.prompt', { target: current.pane, text });
18
+ }
19
+ catch (error) {
20
+ // herdr refuses agent.prompt until it has detected an agent in the pane; type the message instead.
21
+ if (!/not found/.test(errorMessage(error)))
22
+ throw error;
23
+ await herdrCall(current.socket, 'pane.send_input', { pane_id: current.pane, text, keys: ['enter'] });
24
+ }
25
+ },
26
+ };
27
+ }
28
+ }
29
+ /**
30
+ * Connects a push transport to the queue: every new message, and every message still held after a failure, is retried in order.
31
+ * Returns a function that retries the held messages, for example after a new _Agent_Claim_.
32
+ */
33
+ export function attachTransport(queue, transport, log) {
34
+ const push = transport.push;
35
+ if (!push)
36
+ return () => undefined;
37
+ let busy = false;
38
+ let again = false;
39
+ const flush = async () => {
40
+ if (busy) {
41
+ again = true;
42
+ return;
43
+ }
44
+ busy = true;
45
+ try {
46
+ for (const message of queue.list()) {
47
+ try {
48
+ await push(message);
49
+ queue.markDelivered([message.number]);
50
+ }
51
+ catch (error) {
52
+ log(`adoc: ${transport.kind} delivery of message ${message.number} failed: ${errorMessage(error)}`);
53
+ again = false;
54
+ return;
55
+ }
56
+ }
57
+ }
58
+ finally {
59
+ busy = false;
60
+ }
61
+ if (again) {
62
+ again = false;
63
+ await flush();
64
+ }
65
+ };
66
+ queue.on('added', () => void flush());
67
+ return () => void flush();
68
+ }
69
+ //# sourceMappingURL=transports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transports.js","sourceRoot":"","sources":["../src/transports.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAuC,MAAM,eAAe,CAAC;AAgBnF,MAAM,UAAU,eAAe,CAAC,MAAuB,EAAE,KAAK,GAAgB,GAAG,EAAE,CAAC,SAAS;IAC3F,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1B,KAAK,OAAO;YACV,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;oBACtB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;oBACxB,IAAI,CAAC,OAAO;wBAAE,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;oBAC7G,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;oBACpC,IAAI,CAAC;wBACH,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBAClF,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,mGAAmG;wBACnG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;4BAAE,MAAM,KAAK,CAAC;wBACxD,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACvG,CAAC;gBACH,CAAC;aACF,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB,EAAE,SAA2B,EAAE,GAA2B;IAC3G,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAC5B,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;IAClC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,GAAG,IAAI,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC;YACH,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;oBACpB,KAAK,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBACxC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,GAAG,CAAC,SAAS,SAAS,CAAC,IAAI,wBAAwB,OAAO,CAAC,MAAM,YAAY,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBACpG,KAAK,GAAG,KAAK,CAAC;oBACd,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,GAAG,KAAK,CAAC;YACd,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACtC,OAAO,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,182 @@
1
+ import { type ActionEvent, type DocumentSummary } from '@agent-workshop/adoc-plugin-kit';
2
+ import { type AdocConfig } from './config.js';
3
+ import type { AdocHome } from './home.js';
4
+ import { type LoadedPlugin, type Scope } from './plugins.js';
5
+ import { type DocumentRecord, type ScanProblem } from './scan.js';
6
+ import type { ActionMessageInput } from './messages.js';
7
+ /** One entry of a _Document_Summary_List_: the summary, or the key with the parse error, and the last update of the document. */
8
+ export interface SummaryEntry {
9
+ key: string;
10
+ path: string;
11
+ updatedAt: string;
12
+ archived: boolean;
13
+ summary?: DocumentSummary;
14
+ error?: string;
15
+ }
16
+ /** Everything the _Document_Detail_View_ needs for one document. */
17
+ export interface DocumentView {
18
+ key: string;
19
+ pluginKey: string;
20
+ path: string;
21
+ file: string;
22
+ version: string;
23
+ archived: boolean;
24
+ summary?: DocumentSummary;
25
+ summaryError?: string;
26
+ html?: string;
27
+ renderError?: string;
28
+ }
29
+ /** The changes of a document since a base version, from the _Document_Version_Store_. */
30
+ export interface DocumentChanges {
31
+ base: string;
32
+ available: boolean;
33
+ html?: string;
34
+ error?: string;
35
+ }
36
+ /** One version held by the _Document_Version_Store_. */
37
+ export interface StoredVersion {
38
+ version: string;
39
+ seenAt: string;
40
+ current: boolean;
41
+ }
42
+ /** One entry of the report of _Adoc_Check_Command_. */
43
+ export interface CheckEntry {
44
+ level: 'error' | 'warning';
45
+ kind: ScanProblem['kind'] | 'plugin-load' | 'parse-error' | 'broken-reference' | 'no-git' | 'skill-missing' | 'skill-outdated';
46
+ message: string;
47
+ path?: string;
48
+ key?: string;
49
+ }
50
+ export interface PluginInfo {
51
+ key: string;
52
+ /** The _Plugin_Source_ as declared. */
53
+ source: string;
54
+ description?: string;
55
+ layout?: string;
56
+ /** The plugin folder and the scope of the place it was loaded from. */
57
+ directory?: string;
58
+ scope?: Scope;
59
+ /** The name of the _Plugin_Skill_, such as `adoc-kanban`. */
60
+ skill?: string;
61
+ /** The _Plugin_Client_Module_, when the plugin has one: whether it has a stylesheet too. */
62
+ client?: {
63
+ style: boolean;
64
+ };
65
+ error?: string;
66
+ documents: number;
67
+ }
68
+ export type ActionOutcome = {
69
+ status: 'applied' | 'sent';
70
+ version?: string;
71
+ message?: ActionMessageInput;
72
+ } | {
73
+ status: 'refused';
74
+ reason: string;
75
+ } | {
76
+ status: 'failed';
77
+ error: string;
78
+ };
79
+ /**
80
+ * An opened _Adoc_Workspace_: its configuration, loaded plugins and indexed documents.
81
+ * Plugin calls are cached by _Document_Version_; `refresh` rescans after file changes.
82
+ */
83
+ export declare class Workspace {
84
+ readonly home: AdocHome;
85
+ readonly config: AdocConfig;
86
+ private plugins;
87
+ private documents;
88
+ private problems;
89
+ private cache;
90
+ /** _Document_Version_Store_: recent versions of each document, oldest first. */
91
+ private versions;
92
+ private changeCache;
93
+ revision: number;
94
+ /** Whether the workspace is inside a git repository, checked on every refresh. */
95
+ git: boolean;
96
+ private constructor();
97
+ static open(home: AdocHome, env?: NodeJS.ProcessEnv): Promise<Workspace>;
98
+ /** Reloads every plugin from disk, for example after its index.ts changed, and rescans. */
99
+ reloadPlugins(): Promise<void>;
100
+ get root(): string;
101
+ get name(): string;
102
+ /** Rescans the watch paths. Returns the keys whose version changed, appeared or disappeared. */
103
+ refresh(): Promise<string[]>;
104
+ private remember;
105
+ /** The changes of a document since `base`: the plugin's renderChanges, or a line diff of the main file. */
106
+ changes(key: string, base: string): DocumentChanges | undefined;
107
+ /** The versions of a document in the _Document_Version_Store_, newest first. */
108
+ storedVersions(key: string): StoredVersion[] | undefined;
109
+ pluginInfos(): PluginInfo[];
110
+ /** Every declared plugin, loaded or failed, in the order of the _Adoc_Config_. */
111
+ loadedPlugins(): LoadedPlugin[];
112
+ plugin(key: string): LoadedPlugin | undefined;
113
+ record(key: string): DocumentRecord | undefined;
114
+ private definitionOf;
115
+ private entry;
116
+ /** The _Document_Summary_ of a document, computed once per version. */
117
+ summarize(key: string): {
118
+ summary?: DocumentSummary;
119
+ error?: string;
120
+ } | undefined;
121
+ /** The body HTML of a document from its _Plugin_Renderer_, computed once per version. */
122
+ render(key: string): {
123
+ html?: string;
124
+ error?: string;
125
+ } | undefined;
126
+ /** The _Document_Summary_List_ of one plugin, ordered by key. */
127
+ summaryList(pluginKey: string): SummaryEntry[];
128
+ /** _Document_Find_Command_ `list`: the entries of every plugin (or one), archived or not as asked, newest first. */
129
+ findDocuments(options: {
130
+ pluginKey?: string;
131
+ archived: boolean;
132
+ }): Array<SummaryEntry & {
133
+ pluginKey: string;
134
+ }>;
135
+ /** _Document_Find_Command_ `search`: every line of a document file that contains the text, case-insensitive. */
136
+ searchDocuments(text: string, options: {
137
+ pluginKey?: string;
138
+ archived: boolean;
139
+ }): Array<{
140
+ key: string;
141
+ path: string;
142
+ line: number;
143
+ text: string;
144
+ }>;
145
+ /**
146
+ * Writes the main file that the user edited in the _Document_Editor_, when `version` is still current; answers
147
+ * with the new version and the unified diff from `since` (the text the unsent edits started from) to the new text.
148
+ */
149
+ editMainFile(key: string, version: string, text: string, since?: string): Promise<{
150
+ status: 'applied';
151
+ version: string;
152
+ diff: string;
153
+ } | {
154
+ status: 'refused';
155
+ reason: string;
156
+ }>;
157
+ /** The main file of a document with its version, for the elements of a _Plugin_Client_Module_. */
158
+ mainFile(key: string): {
159
+ key: string;
160
+ version: string;
161
+ file: string;
162
+ text: string;
163
+ } | undefined;
164
+ view(key: string): DocumentView | undefined;
165
+ /** _Document_Reference_Resolution_: the summary of the referenced document, or `found: false`. */
166
+ resolve(target: string): {
167
+ found: boolean;
168
+ key: string;
169
+ archived?: boolean;
170
+ summary?: DocumentSummary;
171
+ error?: string;
172
+ };
173
+ /** Every _Document_Reference_ that the renderer of a document marked. */
174
+ references(key: string): string[];
175
+ /** The report of _Adoc_Check_Command_. */
176
+ check(): CheckEntry[];
177
+ /**
178
+ * Handles one _Document_Action_: runs the _Plugin_Action_Handler_ or the _Default_Action_Handler_,
179
+ * refuses a change when the _Document_Version_ moved, writes returned files and returns the message to send.
180
+ */
181
+ applyAction(key: string, event: ActionEvent, version: string): Promise<ActionOutcome>;
182
+ }