@pruddiman/hem 0.0.1-beta-5671db0
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/LICENSE +21 -0
- package/dist/agents/arbiter-agent.d.ts +72 -0
- package/dist/agents/arbiter-agent.js +149 -0
- package/dist/agents/architecture-agent.d.ts +148 -0
- package/dist/agents/architecture-agent.js +459 -0
- package/dist/agents/base-agent.d.ts +44 -0
- package/dist/agents/base-agent.js +57 -0
- package/dist/agents/crossref-agent.d.ts +140 -0
- package/dist/agents/crossref-agent.js +560 -0
- package/dist/agents/crossref-arbiter-agent.d.ts +72 -0
- package/dist/agents/crossref-arbiter-agent.js +147 -0
- package/dist/agents/documentation-agent.d.ts +55 -0
- package/dist/agents/documentation-agent.js +159 -0
- package/dist/agents/exploration-agent.d.ts +58 -0
- package/dist/agents/exploration-agent.js +102 -0
- package/dist/agents/grouping-agent.d.ts +167 -0
- package/dist/agents/grouping-agent.js +557 -0
- package/dist/agents/index-agent.d.ts +86 -0
- package/dist/agents/index-agent.js +360 -0
- package/dist/agents/organization-agent.d.ts +144 -0
- package/dist/agents/organization-agent.js +607 -0
- package/dist/auth.d.ts +372 -0
- package/dist/auth.js +1072 -0
- package/dist/broadcast-mcp.d.ts +21 -0
- package/dist/broadcast-mcp.js +59 -0
- package/dist/changelog.d.ts +85 -0
- package/dist/changelog.js +223 -0
- package/dist/decision-queue.d.ts +173 -0
- package/dist/decision-queue.js +265 -0
- package/dist/diff-scope.d.ts +24 -0
- package/dist/diff-scope.js +28 -0
- package/dist/discovery.d.ts +54 -0
- package/dist/discovery.js +405 -0
- package/dist/grouping.d.ts +37 -0
- package/dist/grouping.js +343 -0
- package/dist/helpers/format.d.ts +5 -0
- package/dist/helpers/format.js +13 -0
- package/dist/helpers/index.d.ts +11 -0
- package/dist/helpers/index.js +11 -0
- package/dist/helpers/parsing.d.ts +52 -0
- package/dist/helpers/parsing.js +128 -0
- package/dist/helpers/paths.d.ts +41 -0
- package/dist/helpers/paths.js +67 -0
- package/dist/helpers/strings.d.ts +45 -0
- package/dist/helpers/strings.js +97 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.js +1087 -0
- package/dist/merge-utils.d.ts +22 -0
- package/dist/merge-utils.js +34 -0
- package/dist/orchestrator.d.ts +194 -0
- package/dist/orchestrator.js +1169 -0
- package/dist/output.d.ts +106 -0
- package/dist/output.js +243 -0
- package/dist/progress.d.ts +228 -0
- package/dist/progress.js +644 -0
- package/dist/providers/copilot.d.ts +247 -0
- package/dist/providers/copilot.js +598 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +12 -0
- package/dist/providers/opencode.d.ts +156 -0
- package/dist/providers/opencode.js +416 -0
- package/dist/providers/types.d.ts +156 -0
- package/dist/providers/types.js +16 -0
- package/dist/resources.d.ts +76 -0
- package/dist/resources.js +151 -0
- package/dist/search-index.d.ts +71 -0
- package/dist/search-index.js +187 -0
- package/dist/search-mcp.d.ts +25 -0
- package/dist/search-mcp.js +100 -0
- package/dist/server-utils.d.ts +56 -0
- package/dist/server-utils.js +135 -0
- package/dist/session.d.ts +227 -0
- package/dist/session.js +370 -0
- package/dist/types.d.ts +272 -0
- package/dist/types.js +5 -0
- package/dist/worktree.d.ts +82 -0
- package/dist/worktree.js +187 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Patrick Ruddiman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arbiter agent for Hem's parallel organization pass.
|
|
3
|
+
*
|
|
4
|
+
* The arbiter is a lightweight coordinator that runs alongside N parallel
|
|
5
|
+
* org workers. It receives all broadcast messages, evaluates SUGGESTION
|
|
6
|
+
* messages, and issues DECISION directives telling specific workers what
|
|
7
|
+
* to do. It does NOT edit files itself.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle (two-phase, driven by OrganizationAgent.runParallel):
|
|
10
|
+
* 1. `run()` — creates the session, sends the initial prompt via
|
|
11
|
+
* promptAsync (fire-and-forget). Returns { sessionId }.
|
|
12
|
+
* 2. `wrapUp()` — called after all workers complete. Sends a final
|
|
13
|
+
* prompt so the arbiter can issue any remaining
|
|
14
|
+
* DECISION messages. Non-fatal on failure.
|
|
15
|
+
*/
|
|
16
|
+
import type { Provider } from "../providers/types.js";
|
|
17
|
+
import { BaseAgent } from "./base-agent.js";
|
|
18
|
+
import type { WorkerAssignment } from "./organization-agent.js";
|
|
19
|
+
/** Parameters for the arbiter prompt. */
|
|
20
|
+
export interface ArbiterPromptParams {
|
|
21
|
+
/** Project name. */
|
|
22
|
+
projectName: string;
|
|
23
|
+
/** Absolute path to the destination directory. */
|
|
24
|
+
destinationPath: string;
|
|
25
|
+
/** ALL documentation files (for full awareness). */
|
|
26
|
+
allDocFiles: string[];
|
|
27
|
+
/** Worker assignments mapping each worker label to its owned files. */
|
|
28
|
+
workerAssignments: WorkerAssignment[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A coordinator agent that monitors worker broadcasts and issues
|
|
32
|
+
* DECISION directives to resolve cross-subset issues.
|
|
33
|
+
*
|
|
34
|
+
* Does NOT edit files — directs workers to make all changes.
|
|
35
|
+
*/
|
|
36
|
+
export declare class ArbiterAgent extends BaseAgent {
|
|
37
|
+
constructor(provider: Provider);
|
|
38
|
+
/**
|
|
39
|
+
* Create the arbiter session and send the initial prompt.
|
|
40
|
+
*
|
|
41
|
+
* The initial prompt is sent via `promptAsync` (fire-and-forget) because
|
|
42
|
+
* the arbiter sits idle until it receives broadcasts from workers.
|
|
43
|
+
*
|
|
44
|
+
* @returns The session ID so the caller can register it in the SSE
|
|
45
|
+
* relay map and later call `wrapUp()`.
|
|
46
|
+
* @throws {AuthExpiredError} If session creation or the initial prompt
|
|
47
|
+
* fails due to authentication expiry.
|
|
48
|
+
*/
|
|
49
|
+
run(params: ArbiterPromptParams, verbose?: (msg: string) => void): Promise<{
|
|
50
|
+
sessionId: string;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Send the wrap-up prompt after all workers have completed.
|
|
54
|
+
*
|
|
55
|
+
* Asks the arbiter to issue any remaining DECISION messages and then
|
|
56
|
+
* output its final `{ "status": "complete" }` response.
|
|
57
|
+
*
|
|
58
|
+
* This is intentionally **non-fatal** — if the arbiter fails, workers
|
|
59
|
+
* have already produced their manifests and the pipeline can continue.
|
|
60
|
+
*/
|
|
61
|
+
wrapUp(sessionId: string, verbose?: (msg: string) => void): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Builds the arbiter prompt.
|
|
64
|
+
*
|
|
65
|
+
* The arbiter is a lightweight coordinator that:
|
|
66
|
+
* - Receives all broadcasts from workers
|
|
67
|
+
* - Evaluates SUGGESTION messages and issues DECISION directives
|
|
68
|
+
* - Directs specific workers to take action on cross-subset issues
|
|
69
|
+
* - Does NOT edit files directly
|
|
70
|
+
*/
|
|
71
|
+
static buildPrompt(params: ArbiterPromptParams): string;
|
|
72
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arbiter agent for Hem's parallel organization pass.
|
|
3
|
+
*
|
|
4
|
+
* The arbiter is a lightweight coordinator that runs alongside N parallel
|
|
5
|
+
* org workers. It receives all broadcast messages, evaluates SUGGESTION
|
|
6
|
+
* messages, and issues DECISION directives telling specific workers what
|
|
7
|
+
* to do. It does NOT edit files itself.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle (two-phase, driven by OrganizationAgent.runParallel):
|
|
10
|
+
* 1. `run()` — creates the session, sends the initial prompt via
|
|
11
|
+
* promptAsync (fire-and-forget). Returns { sessionId }.
|
|
12
|
+
* 2. `wrapUp()` — called after all workers complete. Sends a final
|
|
13
|
+
* prompt so the arbiter can issue any remaining
|
|
14
|
+
* DECISION messages. Non-fatal on failure.
|
|
15
|
+
*/
|
|
16
|
+
import { isAuthExpired, AuthExpiredError } from "../auth.js";
|
|
17
|
+
import { BaseAgent } from "./base-agent.js";
|
|
18
|
+
// ── Agent ───────────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* A coordinator agent that monitors worker broadcasts and issues
|
|
21
|
+
* DECISION directives to resolve cross-subset issues.
|
|
22
|
+
*
|
|
23
|
+
* Does NOT edit files — directs workers to make all changes.
|
|
24
|
+
*/
|
|
25
|
+
export class ArbiterAgent extends BaseAgent {
|
|
26
|
+
constructor(provider) {
|
|
27
|
+
super(provider);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create the arbiter session and send the initial prompt.
|
|
31
|
+
*
|
|
32
|
+
* The initial prompt is sent via `promptAsync` (fire-and-forget) because
|
|
33
|
+
* the arbiter sits idle until it receives broadcasts from workers.
|
|
34
|
+
*
|
|
35
|
+
* @returns The session ID so the caller can register it in the SSE
|
|
36
|
+
* relay map and later call `wrapUp()`.
|
|
37
|
+
* @throws {AuthExpiredError} If session creation or the initial prompt
|
|
38
|
+
* fails due to authentication expiry.
|
|
39
|
+
*/
|
|
40
|
+
async run(params, verbose) {
|
|
41
|
+
const tag = "arbiter";
|
|
42
|
+
// 1. Build prompt
|
|
43
|
+
const prompt = ArbiterAgent.buildPrompt(params);
|
|
44
|
+
if (verbose) {
|
|
45
|
+
verbose(`[${tag}] prompt ${prompt.length.toLocaleString()} chars`);
|
|
46
|
+
}
|
|
47
|
+
// 2. Create session
|
|
48
|
+
const sessionId = await this.createSession("Hem: arbiter");
|
|
49
|
+
if (verbose) {
|
|
50
|
+
verbose(`[${tag}] session ${sessionId}`);
|
|
51
|
+
}
|
|
52
|
+
// 3. Fire the initial prompt (fire-and-forget — arbiter waits for broadcasts)
|
|
53
|
+
try {
|
|
54
|
+
await this.provider.session.promptAsync({
|
|
55
|
+
path: { id: sessionId },
|
|
56
|
+
body: {
|
|
57
|
+
parts: [{ type: "text", text: prompt }],
|
|
58
|
+
agent: "hem-org",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (isAuthExpired(err)) {
|
|
64
|
+
throw new AuthExpiredError("the configured provider", err);
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
if (verbose) {
|
|
69
|
+
verbose(`[${tag}] initial prompt sent (listening for broadcasts)`);
|
|
70
|
+
}
|
|
71
|
+
return { sessionId };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Send the wrap-up prompt after all workers have completed.
|
|
75
|
+
*
|
|
76
|
+
* Asks the arbiter to issue any remaining DECISION messages and then
|
|
77
|
+
* output its final `{ "status": "complete" }` response.
|
|
78
|
+
*
|
|
79
|
+
* This is intentionally **non-fatal** — if the arbiter fails, workers
|
|
80
|
+
* have already produced their manifests and the pipeline can continue.
|
|
81
|
+
*/
|
|
82
|
+
async wrapUp(sessionId, verbose) {
|
|
83
|
+
const tag = "arbiter";
|
|
84
|
+
if (verbose) {
|
|
85
|
+
verbose(`[${tag}] all workers done, sending wrap-up prompt`);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const wrapUpResponse = await this.provider.prompt(sessionId, "All workers have completed their tasks. If you have any remaining " +
|
|
89
|
+
"DECISION messages to issue, broadcast them now. Otherwise, output your " +
|
|
90
|
+
'final `{ "status": "complete" }` response.', { agent: "hem-org" });
|
|
91
|
+
if (verbose) {
|
|
92
|
+
verbose(`[${tag}] wrap-up response (${(wrapUpResponse ?? "").length} chars)`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// Arbiter failure is not fatal — workers already produced manifests
|
|
97
|
+
if (verbose) {
|
|
98
|
+
verbose(`[${tag}] ⚠ wrap-up failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Builds the arbiter prompt.
|
|
104
|
+
*
|
|
105
|
+
* The arbiter is a lightweight coordinator that:
|
|
106
|
+
* - Receives all broadcasts from workers
|
|
107
|
+
* - Evaluates SUGGESTION messages and issues DECISION directives
|
|
108
|
+
* - Directs specific workers to take action on cross-subset issues
|
|
109
|
+
* - Does NOT edit files directly
|
|
110
|
+
*/
|
|
111
|
+
static buildPrompt(params) {
|
|
112
|
+
const { projectName, destinationPath, allDocFiles, workerAssignments, } = params;
|
|
113
|
+
const totalWorkers = workerAssignments.length;
|
|
114
|
+
const workerLabels = workerAssignments.map((a) => a.label);
|
|
115
|
+
const parts = [];
|
|
116
|
+
// ── Role ──────────────────────────────────────────────────────────
|
|
117
|
+
parts.push(`Coordinate a parallel documentation organization pass on`, `**${projectName}** as the **arbiter**. There are ${totalWorkers} workers running in parallel,`, `each responsible for a subset of files.`, "", `Coordinate only — do NOT edit files. Workers have the edit tool`, `and make all file changes themselves. Issue DECISION messages to tell`, `workers what to do.`, "", `**IMPORTANT: Workers are terminated immediately after they finish.**`, `If a worker has already completed and you need further changes`, `to their files, use the RECALL mechanism (see below).`, "");
|
|
118
|
+
// ── Workers ───────────────────────────────────────────────────────
|
|
119
|
+
parts.push("## Workers", "", `There are ${totalWorkers} workers: ${workerLabels.join(", ")}`, "");
|
|
120
|
+
// ── Destination directory ─────────────────────────────────────────
|
|
121
|
+
parts.push("## Destination directory", "", `All documentation files are in: \`${destinationPath}\``, "");
|
|
122
|
+
// ── Worker file assignments ─────────────────────────────────────
|
|
123
|
+
parts.push("## Worker file assignments", "", "Each worker owns a specific subset of files. When issuing DECISION", "directives, always target the worker that owns the affected file(s).", "");
|
|
124
|
+
for (const { label, files } of workerAssignments) {
|
|
125
|
+
parts.push(`### ${label}`, "");
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
parts.push(`- \`${destinationPath}/${file}\``);
|
|
128
|
+
}
|
|
129
|
+
parts.push("");
|
|
130
|
+
}
|
|
131
|
+
// ── All files (flat list for cross-reference) ─────────────────────
|
|
132
|
+
parts.push("## All documentation files", "");
|
|
133
|
+
for (const file of allDocFiles) {
|
|
134
|
+
parts.push(`- \`${destinationPath}/${file}\``);
|
|
135
|
+
}
|
|
136
|
+
parts.push("");
|
|
137
|
+
// ── How it works ──────────────────────────────────────────────────
|
|
138
|
+
parts.push("## How this works", "", "Workers broadcast messages as they work. You will receive these messages", "as user messages prefixed with \"Message from org-worker-N:\". Message types:", "", "- **RENAMED**: A worker renamed/moved a file in their subset.", "- **DELETED**: A worker deleted a file (merged into another).", "- **CONVENTION**: A worker established a naming/structure convention.", "- **SUGGESTION**: A worker identified a cross-subset issue they cannot", " resolve themselves (e.g., duplicate files across different workers).", "- **ACK**: A worker completed an action (edited files, updated links).", "");
|
|
139
|
+
// ── Your job ──────────────────────────────────────────────────────
|
|
140
|
+
parts.push("## Your job", "", "1. **Monitor all broadcasts.** You will passively receive RENAMED, DELETED,", " CONVENTION, and ACK messages — you do not need to respond to these unless", " you see a conflict or problem.", "", "2. **Act on SUGGESTION messages.** When a worker broadcasts a SUGGESTION", " (e.g., \"these two files across different workers look like duplicates\"),", " evaluate it and issue a DECISION telling the appropriate worker(s) what to do.", "", "3. **Issue DECISION messages.** Use the broadcast tool with one of these", " structured formats. The orchestrator parses these to sequence operations", " correctly (e.g., ensuring MERGE completes before DELETE on the same file).", "", " **MERGE** — tell a worker to merge content from a source file into a target:", "", ' `broadcast({ message: "DECISION: @org-worker-2 MERGE auth/api-keys.md INTO auth/authentication-overview.md\\nMerge all API key setup instructions into the authentication overview." })`', "", " **DELETE** — tell a worker to delete a file:", "", ' `broadcast({ message: "DECISION: @org-worker-2 DELETE auth/api-keys.md\\nThis file has been merged into auth/authentication-overview.md." })`', "", " **UPDATE-LINKS** — tell a worker to update internal links in a file:", "", ' `broadcast({ message: "DECISION: @org-worker-3 UPDATE-LINKS guides/setup.md\\nUpdate the link to auth/api-keys.md → auth/authentication-overview.md" })`', "", " **Free-form** — for anything that doesn't fit the above patterns:", "", ' `broadcast({ message: "DECISION: @org-worker-1 Rename the \'overview\' section heading to \'Introduction\' for consistency" })`', "", " **Rules for DECISION messages:**", " - **ALWAYS** address decisions to a specific worker using `@org-worker-N`.", " - Messages without an `@` tag are broadcast to ALL workers, which wastes", " resources. Only use `@all-workers` when every worker needs to act.", " - When resolving duplicates, issue the MERGE first, then the DELETE.", " The orchestrator will automatically hold the DELETE until the MERGE", " completes.", " - If multiple workers need to act, issue multiple decisions each tagged", " to the specific worker.", "", "4. **Resolve conflicts.** If two workers propose contradictory conventions", " or make conflicting changes, issue a DECISION picking one approach.", "", "5. **Never edit files.** This is a coordination task, not editing. Workers have", " the edit tool and make all file changes themselves.", "");
|
|
141
|
+
// ── RECALL mechanism ──────────────────────────────────────────────
|
|
142
|
+
parts.push("## RECALL mechanism", "", "Workers are terminated immediately after they finish. If", "you discover during wrap-up that a worker's files need further changes", "(e.g., you spot issues after all workers have completed), you can recall", "a worker by broadcasting a message with this exact prefix:", "", ' `broadcast({ message: "RECALL: @org-worker-2 Fix the broken links in auth/overview.md — update references to the renamed api/endpoints.md" })`', "", "The orchestrator will intercept this message and spawn a **new** session", "with that worker's original file assignment plus your fix instructions.", "The recalled worker will make the edits directly.", "", "Rules for RECALL:", "- Use `RECALL:` prefix followed by `@org-worker-N` and clear instructions.", "- Only recall workers whose files actually need changes.", "- Be specific about what needs to be fixed — the recalled worker gets", " your instructions verbatim.", "");
|
|
143
|
+
// ── Decision guidelines ───────────────────────────────────────────
|
|
144
|
+
parts.push("## Decision guidelines", "", "- When resolving duplicate files across workers, issue **two separate**", " DECISION messages: first a MERGE telling the worker who owns the", " CANONICAL file to merge content from the duplicate, then a DELETE", " telling the worker who owns the duplicate to delete it entirely.", " The orchestrator ensures the DELETE waits for the MERGE to complete.", " Do NOT instruct workers to create redirect stubs or placeholder files —", " duplicates must be fully removed.", "", "- When a rename or deletion affects links in other workers' files, issue", " an UPDATE-LINKS DECISION to each affected worker.", "", "- Keep decisions specific and actionable. Include file paths and what", " exactly should be done.", "");
|
|
145
|
+
// ── Output format ─────────────────────────────────────────────────
|
|
146
|
+
parts.push("## Output format", "", "Wait for worker broadcasts. Process them as described above. When you are", "finished coordinating (no pending suggestions to resolve), respond with", "ONLY:", "", "```json", '{ "status": "complete" }', "```");
|
|
147
|
+
return parts.join("\n");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-assisted architecture overview generation agent for Hem.
|
|
3
|
+
*
|
|
4
|
+
* Post-processing agent that runs AFTER cross-ref and organization agents.
|
|
5
|
+
* Uses an OpenCode session to generate the top-level `architecture.md`
|
|
6
|
+
* document by reading all generated documentation files and exploration
|
|
7
|
+
* findings.
|
|
8
|
+
*
|
|
9
|
+
* Architecture (v2):
|
|
10
|
+
* - The agent writes `architecture.md` directly via the edit tool.
|
|
11
|
+
* - The pipeline discovers what was written by scanning disk afterward.
|
|
12
|
+
* - Reads existing generated docs from disk for context.
|
|
13
|
+
* - Runs as the last content agent in the pipeline (after org + xref).
|
|
14
|
+
*/
|
|
15
|
+
import type { Provider } from "../providers/types.js";
|
|
16
|
+
import type { ExplorationFindings } from "../types.js";
|
|
17
|
+
import { BaseAgent } from "./base-agent.js";
|
|
18
|
+
/**
|
|
19
|
+
* Maximum character count for the full architecture prompt before
|
|
20
|
+
* switching to chunked mode. Set conservatively below typical LLM
|
|
21
|
+
* context windows (~200K tokens ≈ ~800K chars) to leave room for
|
|
22
|
+
* the agent's tool interactions and responses.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ARCH_PROMPT_CHAR_LIMIT = 400000;
|
|
25
|
+
/**
|
|
26
|
+
* Target character count per chunk when splitting findings and group
|
|
27
|
+
* summaries for chunked architecture generation. Each chunk should
|
|
28
|
+
* comfortably fit in a single prompt alongside instructions.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ARCH_CHUNK_TARGET_CHARS = 150000;
|
|
31
|
+
/** Group summary for architecture prompt input. */
|
|
32
|
+
export interface ArchGroupSummary {
|
|
33
|
+
id: string;
|
|
34
|
+
label: string;
|
|
35
|
+
files: string[];
|
|
36
|
+
}
|
|
37
|
+
/** Summary produced by a chunk summarization pass. */
|
|
38
|
+
export interface ArchChunkSummary {
|
|
39
|
+
/** Index of the chunk (0-based). */
|
|
40
|
+
chunkIndex: number;
|
|
41
|
+
/** Key architectural patterns discovered in this chunk. */
|
|
42
|
+
patterns: string[];
|
|
43
|
+
/** Cross-cutting concerns identified. */
|
|
44
|
+
crossCuttingConcerns: string[];
|
|
45
|
+
/** Key integrations and external dependencies. */
|
|
46
|
+
integrations: string[];
|
|
47
|
+
/** System components and their responsibilities. */
|
|
48
|
+
components: string[];
|
|
49
|
+
/** Significant data flows or interaction patterns. */
|
|
50
|
+
dataFlows: string[];
|
|
51
|
+
/** Raw summary text from the LLM. */
|
|
52
|
+
rawSummary: string;
|
|
53
|
+
}
|
|
54
|
+
/** Parameters for the architecture overview prompt. */
|
|
55
|
+
export interface ArchPromptParams {
|
|
56
|
+
projectName: string;
|
|
57
|
+
sourceRoot: string;
|
|
58
|
+
destinationPath: string;
|
|
59
|
+
allFindings: ExplorationFindings[];
|
|
60
|
+
allGroupSummaries: ArchGroupSummary[];
|
|
61
|
+
/** Paths of all generated doc files (for reading context). */
|
|
62
|
+
allDocFiles: string[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* An agent that uses an LLM to generate the architecture overview
|
|
66
|
+
* document for a project's documentation.
|
|
67
|
+
*
|
|
68
|
+
* Writes `architecture.md` directly via the edit tool. The pipeline
|
|
69
|
+
* discovers what was written by scanning disk afterward.
|
|
70
|
+
*/
|
|
71
|
+
export declare class ArchitectureAgent extends BaseAgent {
|
|
72
|
+
constructor(provider: Provider);
|
|
73
|
+
/**
|
|
74
|
+
* Run the architecture overview generation pipeline.
|
|
75
|
+
*
|
|
76
|
+
* When the full prompt would exceed `ARCH_PROMPT_CHAR_LIMIT`, the agent
|
|
77
|
+
* automatically switches to chunked mode: findings and group summaries
|
|
78
|
+
* are split into manageable batches, each batch is summarised by a
|
|
79
|
+
* dedicated session, and a final synthesis session combines the chunk
|
|
80
|
+
* summaries into the architecture overview.
|
|
81
|
+
*
|
|
82
|
+
* @param params - All inputs needed for the architecture prompt.
|
|
83
|
+
* @param verbose - Optional logging callback (writes to stderr).
|
|
84
|
+
* @throws If session creation or prompting fails.
|
|
85
|
+
*/
|
|
86
|
+
run(params: ArchPromptParams, verbose?: (msg: string) => void): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Chunked architecture generation for large projects.
|
|
89
|
+
*
|
|
90
|
+
* 1. Splits exploration findings and group summaries into chunks that
|
|
91
|
+
* fit within context limits.
|
|
92
|
+
* 2. Runs a sequential summarisation session for each chunk.
|
|
93
|
+
* 3. Runs a final synthesis session that combines all chunk summaries
|
|
94
|
+
* and writes `architecture.md` via the edit tool.
|
|
95
|
+
*/
|
|
96
|
+
private runChunked;
|
|
97
|
+
/**
|
|
98
|
+
* Builds the architecture overview prompt from exploration findings
|
|
99
|
+
* and group summaries.
|
|
100
|
+
*/
|
|
101
|
+
static buildPrompt(params: ArchPromptParams): string;
|
|
102
|
+
/**
|
|
103
|
+
* Formats all exploration findings for the architecture prompt.
|
|
104
|
+
*/
|
|
105
|
+
static formatAllFindings(allFindings: ExplorationFindings[]): string;
|
|
106
|
+
/**
|
|
107
|
+
* Formats file group summaries as a list for the prompt.
|
|
108
|
+
*/
|
|
109
|
+
static formatGroupSummaries(groups: ArchGroupSummary[]): string;
|
|
110
|
+
/**
|
|
111
|
+
* Split exploration findings and group summaries into chunks that each
|
|
112
|
+
* stay under `targetChars` when formatted.
|
|
113
|
+
*
|
|
114
|
+
* Findings and their corresponding group summaries are kept together.
|
|
115
|
+
* Groups without findings are distributed across chunks to balance
|
|
116
|
+
* size.
|
|
117
|
+
*
|
|
118
|
+
* @returns Array of chunks, each with a subset of findings and groups.
|
|
119
|
+
*/
|
|
120
|
+
static chunkFindings(allFindings: ExplorationFindings[], allGroupSummaries: ArchGroupSummary[], targetChars: number): Array<{
|
|
121
|
+
findings: ExplorationFindings[];
|
|
122
|
+
groups: ArchGroupSummary[];
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Build a prompt for summarising a single chunk of findings and groups.
|
|
126
|
+
*
|
|
127
|
+
* The LLM should return a structured JSON summary that can be fed
|
|
128
|
+
* into the synthesis phase.
|
|
129
|
+
*/
|
|
130
|
+
static buildChunkSummaryPrompt(projectName: string, findings: ExplorationFindings[], groups: ArchGroupSummary[], chunkIndex: number, totalChunks: number): string;
|
|
131
|
+
/**
|
|
132
|
+
* Parse the LLM response from a chunk summarisation session into an
|
|
133
|
+
* `ArchChunkSummary`.
|
|
134
|
+
*
|
|
135
|
+
* Falls back to a minimal summary containing just the raw response
|
|
136
|
+
* text if JSON extraction fails — chunked mode should be resilient.
|
|
137
|
+
*/
|
|
138
|
+
static parseChunkSummary(response: string, chunkIndex: number): ArchChunkSummary;
|
|
139
|
+
/**
|
|
140
|
+
* Build the final synthesis prompt that combines all chunk summaries
|
|
141
|
+
* and instructs the agent to write `architecture.md`.
|
|
142
|
+
*
|
|
143
|
+
* Reuses the same quality standards and document structure as the
|
|
144
|
+
* normal-mode `buildPrompt()`, but substitutes chunk summaries for
|
|
145
|
+
* the raw findings.
|
|
146
|
+
*/
|
|
147
|
+
static buildSynthesisPrompt(params: ArchPromptParams, chunkSummaries: ArchChunkSummary[]): string;
|
|
148
|
+
}
|