@pruddiman/hem 0.0.1-beta-6f925fe → 0.0.1-beta-697e946
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.
|
@@ -119,8 +119,11 @@ export class DocumentationAgent extends BaseAgent {
|
|
|
119
119
|
// 1. System-level instructions
|
|
120
120
|
parts.push(`Generate documentation files that answer the questions the code asks — not`, `merely describe what the code does.`, "", `**You write files directly using the edit tool.** Do NOT return Markdown content`, `in your response text. Instead, use the edit tool to create and write files in`, `the destination directory. When you are done writing all files, stop.`, "");
|
|
121
121
|
// 2. Where to write files
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
// Sub-agents (IDs like `parent:sub-3`) share the parent group's folder —
|
|
123
|
+
// otherwise every chunk of a large group creates its own sibling dir.
|
|
124
|
+
const parentGroupId = group.id.split(":")[0] ?? group.id;
|
|
125
|
+
const groupSubfolder = `${context.destinationPath}/${parentGroupId}`;
|
|
126
|
+
parts.push("## Destination directory", "", `Write ALL documentation files for this group under this exact subfolder:`, "", ` \`${groupSubfolder}/\``, "", `**Do NOT** write outside this subfolder. **Do NOT** write to the root`, `\`${context.destinationPath}\` directory or any sibling group's subfolder.`, `This keeps the docs tree organized one-subfolder-per-group.`, "", `Within \`${groupSubfolder}/\` you have full autonomy over:`, `- **File naming**: Choose descriptive kebab-case filenames (e.g., \`overview.md\`, \`api-reference.md\`).`, `- **Nested subdirectories**: Create child folders inside the group subfolder if it helps (e.g., \`${groupSubfolder}/guides/getting-started.md\`).`, `- **Number of files**: Create as many files as needed to properly document the group.`, `- **File structure**: Design each document's heading hierarchy and sections.`, "", `Use \`.md\` extension for all files.`, "", `### How to create the subfolder`, "", `Run \`mkdir -p ${groupSubfolder}\` once at the start using the bash tool, then`, `use the edit tool to write each \`.md\` file into that folder. Do NOT write a`, `shell script, Python script, or any other helper file to create directories —`, `the bash tool is allowed to run \`mkdir\` directly, and the edit tool handles`, `everything else.`, "");
|
|
124
127
|
// 3. Quality standards
|
|
125
128
|
parts.push("## Quality Standard: Answer Every Question", "", "Your primary quality standard is: **every question from the exploration findings", "MUST be answered in the generated documentation.** Do NOT leave any question", "unanswered. If you cannot find a definitive answer, state what is known and what", "requires further investigation — but NEVER silently skip a question.", "", "For each integration discovered in the exploration findings:", "", "1. **Use `webfetch` to research answers**: When an integration has an", " `officialDocsUrl`, fetch that URL to find authoritative answers.", "2. **Address HOW, not just WHAT**: Explain HOW to access, query, monitor,", " and troubleshoot each integration.", "3. **Answer operational questions**: Every `operationalQuestions` entry for each", " integration MUST be answered in the documentation.", "");
|
|
126
129
|
parts.push("## Documentation quality standards", "");
|
package/dist/grouping.js
CHANGED
|
@@ -137,15 +137,6 @@ export function commonDirectory(files) {
|
|
|
137
137
|
}
|
|
138
138
|
return common.length === 0 ? "." : common.join("/");
|
|
139
139
|
}
|
|
140
|
-
/**
|
|
141
|
-
* Extracts the file stem (name without the final extension).
|
|
142
|
-
* For multi-part suffixes like `user.controller.ts`, the stem is `user`.
|
|
143
|
-
*/
|
|
144
|
-
function fileStem(relativePath) {
|
|
145
|
-
const base = relativePath.split("/").pop() ?? "";
|
|
146
|
-
const dotIndex = base.indexOf(".");
|
|
147
|
-
return dotIndex === -1 ? base : base.substring(0, dotIndex);
|
|
148
|
-
}
|
|
149
140
|
/**
|
|
150
141
|
* Returns the "name" portion of the file without known layer suffixes
|
|
151
142
|
* and without the actual file extension.
|
|
@@ -387,7 +378,7 @@ export function groupFiles(files, options = {}) {
|
|
|
387
378
|
for (const [key, bucket] of featureBuckets) {
|
|
388
379
|
const label = featureLabels.get(key) ?? toDisplayLabel(key);
|
|
389
380
|
groups.push({
|
|
390
|
-
id: toKebabCase(label)
|
|
381
|
+
id: toKebabCase(label),
|
|
391
382
|
label,
|
|
392
383
|
type: "vertical",
|
|
393
384
|
files: bucket.sort((a, b) => a.path.localeCompare(b.path)),
|
|
@@ -396,6 +387,10 @@ export function groupFiles(files, options = {}) {
|
|
|
396
387
|
}
|
|
397
388
|
for (const [label, bucket] of layerBuckets) {
|
|
398
389
|
groups.push({
|
|
390
|
+
// Layer groups keep the `-layer` suffix because they're a different
|
|
391
|
+
// architectural concept than feature verticals, and without it an
|
|
392
|
+
// "Auth" feature + "Controllers" layer + "Controllers" feature prior
|
|
393
|
+
// could collide on ID.
|
|
399
394
|
id: toKebabCase(label) + "-layer",
|
|
400
395
|
label,
|
|
401
396
|
type: "horizontal",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { dirname, join } from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
-
// ──
|
|
23
|
+
// ── Bash command allowlists ─────────────────────────────────────────────
|
|
24
24
|
/**
|
|
25
25
|
* Base commands allowed in read-only bash mode.
|
|
26
26
|
* Mirrors the READ_ONLY_BASH config in OpenCodeProvider.
|
|
@@ -29,6 +29,16 @@ const READ_ONLY_CMDS = new Set([
|
|
|
29
29
|
"cat", "head", "tail", "grep", "find", "ls",
|
|
30
30
|
"wc", "file", "tree", "du",
|
|
31
31
|
]);
|
|
32
|
+
/**
|
|
33
|
+
* Filesystem-setup commands writing agents are allowed to run. Mirrors
|
|
34
|
+
* WRITING_AGENT_BASH in OpenCodeProvider. These are needed so agents can
|
|
35
|
+
* create their per-group output subfolder without falling back to writing
|
|
36
|
+
* throwaway scripts.
|
|
37
|
+
*/
|
|
38
|
+
const WRITING_AGENT_CMDS = new Set([
|
|
39
|
+
...READ_ONLY_CMDS,
|
|
40
|
+
"mkdir", "rmdir", "touch", "echo", "mv", "cp",
|
|
41
|
+
]);
|
|
32
42
|
/**
|
|
33
43
|
* Check whether a shell command is read-only (safe to run without write access).
|
|
34
44
|
* Matches READ_ONLY_BASH from OpenCodeProvider:
|
|
@@ -48,6 +58,28 @@ function isReadOnlyCommand(cmd) {
|
|
|
48
58
|
}
|
|
49
59
|
return false;
|
|
50
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Check whether a shell command is allowed for writing agents. Includes
|
|
63
|
+
* the read-only set plus filesystem-setup commands (mkdir, touch, etc.).
|
|
64
|
+
*/
|
|
65
|
+
function isWritingAgentCommand(cmd) {
|
|
66
|
+
if (isReadOnlyCommand(cmd))
|
|
67
|
+
return true;
|
|
68
|
+
const base = cmd.trim().split(/\s+/)[0] ?? "";
|
|
69
|
+
return WRITING_AGENT_CMDS.has(base);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Writing agents that are allowed to mkdir / touch / mv / cp for
|
|
73
|
+
* scaffolding their per-group output subfolder. Matches the OpenCode
|
|
74
|
+
* `writingAgentPermission` / `orgAgentPermission` list.
|
|
75
|
+
*/
|
|
76
|
+
const WRITING_AGENTS = new Set([
|
|
77
|
+
"hem-doc",
|
|
78
|
+
"hem-arch",
|
|
79
|
+
"hem-index",
|
|
80
|
+
"hem-org",
|
|
81
|
+
"hem-xref",
|
|
82
|
+
]);
|
|
51
83
|
/** Environment variable names checked for GitHub token, in priority order. */
|
|
52
84
|
const TOKEN_ENV_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"];
|
|
53
85
|
/** Resolve the broadcast MCP server path relative to this compiled module. */
|
|
@@ -76,6 +108,10 @@ function agentAllowsShell(agentName, command) {
|
|
|
76
108
|
// hem-group uses READ_ONLY_BASH too (bash is explicitly read-only, not "deny")
|
|
77
109
|
if (isReadOnlyCommand(command))
|
|
78
110
|
return true;
|
|
111
|
+
// Writing agents (hem-doc / hem-arch / hem-index / hem-org / hem-xref)
|
|
112
|
+
// also need mkdir/touch/mv/cp/echo/rmdir to scaffold their output folder.
|
|
113
|
+
if (WRITING_AGENTS.has(agentName) && isWritingAgentCommand(command))
|
|
114
|
+
return true;
|
|
79
115
|
// hem-org additionally allows rm (mirrors ORG_AGENT_BASH)
|
|
80
116
|
if (agentName === "hem-org" && command.trim().split(/\s+/)[0] === "rm")
|
|
81
117
|
return true;
|
|
@@ -40,14 +40,52 @@ export declare const READ_ONLY_BASH: {
|
|
|
40
40
|
readonly "git diff *": "allow";
|
|
41
41
|
readonly "*": "deny";
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Bash permission set for writing agents (hem-doc, hem-arch, hem-index,
|
|
45
|
+
* hem-xref). Inherits read-only inspection commands and additionally
|
|
46
|
+
* allows the minimum set of filesystem-setup commands agents need to
|
|
47
|
+
* create per-group subfolders and scaffold empty files without resorting
|
|
48
|
+
* to writing throwaway scripts (e.g. a Python `make_dir.py`).
|
|
49
|
+
*
|
|
50
|
+
* Writing agents with only READ_ONLY_BASH got stuck in a loop trying to
|
|
51
|
+
* mkdir their output directory via the Copilot shell tool, being denied,
|
|
52
|
+
* and then fabricating Python to work around it.
|
|
53
|
+
*/
|
|
54
|
+
export declare const WRITING_AGENT_BASH: {
|
|
55
|
+
readonly "mkdir *": "allow";
|
|
56
|
+
readonly "rmdir *": "allow";
|
|
57
|
+
readonly "touch *": "allow";
|
|
58
|
+
readonly "echo *": "allow";
|
|
59
|
+
readonly "mv *": "allow";
|
|
60
|
+
readonly "cp *": "allow";
|
|
61
|
+
readonly "cat *": "allow";
|
|
62
|
+
readonly "head *": "allow";
|
|
63
|
+
readonly "tail *": "allow";
|
|
64
|
+
readonly "grep *": "allow";
|
|
65
|
+
readonly "find *": "allow";
|
|
66
|
+
readonly "ls *": "allow";
|
|
67
|
+
readonly "wc *": "allow";
|
|
68
|
+
readonly "file *": "allow";
|
|
69
|
+
readonly "tree *": "allow";
|
|
70
|
+
readonly "du *": "allow";
|
|
71
|
+
readonly "git status *": "allow";
|
|
72
|
+
readonly "git diff *": "allow";
|
|
73
|
+
readonly "*": "deny";
|
|
74
|
+
};
|
|
43
75
|
/**
|
|
44
76
|
* Extended bash permission set for organization workers. Inherits all
|
|
45
|
-
*
|
|
46
|
-
* `rm` so workers can delete files when executing arbiter DELETE
|
|
47
|
-
* instead of writing empty content as a deletion proxy.
|
|
77
|
+
* writing-agent commands from {@link WRITING_AGENT_BASH} and additionally
|
|
78
|
+
* allows `rm` so workers can delete files when executing arbiter DELETE
|
|
79
|
+
* decisions instead of writing empty content as a deletion proxy.
|
|
48
80
|
*/
|
|
49
81
|
export declare const ORG_AGENT_BASH: {
|
|
50
82
|
readonly "rm *": "allow";
|
|
83
|
+
readonly "mkdir *": "allow";
|
|
84
|
+
readonly "rmdir *": "allow";
|
|
85
|
+
readonly "touch *": "allow";
|
|
86
|
+
readonly "echo *": "allow";
|
|
87
|
+
readonly "mv *": "allow";
|
|
88
|
+
readonly "cp *": "allow";
|
|
51
89
|
readonly "cat *": "allow";
|
|
52
90
|
readonly "head *": "allow";
|
|
53
91
|
readonly "tail *": "allow";
|
|
@@ -43,14 +43,34 @@ export const READ_ONLY_BASH = {
|
|
|
43
43
|
"git diff *": "allow",
|
|
44
44
|
"*": "deny",
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Bash permission set for writing agents (hem-doc, hem-arch, hem-index,
|
|
48
|
+
* hem-xref). Inherits read-only inspection commands and additionally
|
|
49
|
+
* allows the minimum set of filesystem-setup commands agents need to
|
|
50
|
+
* create per-group subfolders and scaffold empty files without resorting
|
|
51
|
+
* to writing throwaway scripts (e.g. a Python `make_dir.py`).
|
|
52
|
+
*
|
|
53
|
+
* Writing agents with only READ_ONLY_BASH got stuck in a loop trying to
|
|
54
|
+
* mkdir their output directory via the Copilot shell tool, being denied,
|
|
55
|
+
* and then fabricating Python to work around it.
|
|
56
|
+
*/
|
|
57
|
+
export const WRITING_AGENT_BASH = {
|
|
58
|
+
...READ_ONLY_BASH,
|
|
59
|
+
"mkdir *": "allow",
|
|
60
|
+
"rmdir *": "allow",
|
|
61
|
+
"touch *": "allow",
|
|
62
|
+
"echo *": "allow",
|
|
63
|
+
"mv *": "allow",
|
|
64
|
+
"cp *": "allow",
|
|
65
|
+
};
|
|
46
66
|
/**
|
|
47
67
|
* Extended bash permission set for organization workers. Inherits all
|
|
48
|
-
*
|
|
49
|
-
* `rm` so workers can delete files when executing arbiter DELETE
|
|
50
|
-
* instead of writing empty content as a deletion proxy.
|
|
68
|
+
* writing-agent commands from {@link WRITING_AGENT_BASH} and additionally
|
|
69
|
+
* allows `rm` so workers can delete files when executing arbiter DELETE
|
|
70
|
+
* decisions instead of writing empty content as a deletion proxy.
|
|
51
71
|
*/
|
|
52
72
|
export const ORG_AGENT_BASH = {
|
|
53
|
-
...
|
|
73
|
+
...WRITING_AGENT_BASH,
|
|
54
74
|
"rm *": "allow",
|
|
55
75
|
};
|
|
56
76
|
// ── OpenCode Provider ───────────────────────────────────────────────────
|
|
@@ -183,10 +203,11 @@ export class OpenCodeProvider {
|
|
|
183
203
|
// modelID may already include a sub-provider prefix (e.g. "github-copilot/opus-4.6")
|
|
184
204
|
// when the user picked an opencode sub-provider model via `hem config`. Use it as-is.
|
|
185
205
|
: modelID.includes("/") ? modelID : `${providerID}/${modelID}`;
|
|
186
|
-
// Writing agents get scoped access to the destination directory
|
|
206
|
+
// Writing agents get scoped access to the destination directory plus
|
|
207
|
+
// the filesystem-setup commands they need to create group subfolders.
|
|
187
208
|
const writingAgentPermission = {
|
|
188
209
|
edit: "allow",
|
|
189
|
-
bash:
|
|
210
|
+
bash: WRITING_AGENT_BASH,
|
|
190
211
|
webfetch: "allow",
|
|
191
212
|
external_directory: "allow",
|
|
192
213
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export interface FileInfo {
|
|
|
71
71
|
}
|
|
72
72
|
/** A collection of related source files to be documented together. */
|
|
73
73
|
export interface FileGroup {
|
|
74
|
-
/** Unique group identifier (e.g., `user
|
|
74
|
+
/** Unique group identifier (e.g., `user`, `auth`, `controllers-layer`). */
|
|
75
75
|
id: string;
|
|
76
76
|
/** Human-readable group name (e.g., "User Feature", "Controllers"). */
|
|
77
77
|
label: string;
|