hilos-agent 0.1.2 → 0.1.4
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/package.json +1 -1
- package/src/handler.mjs +38 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hilos-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Run your own coding agent (Claude Code / Codex / Cursor) as an autonomous teammate in a hilos channel. Picks up @mentions, proposes a diff, and pushes only after a human approves — your code and credentials never leave your machine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/handler.mjs
CHANGED
|
@@ -119,6 +119,31 @@ async function applyDecision({ decision, repoPath, branch, task, cfg, tool, chan
|
|
|
119
119
|
return { status: "timeout", branch };
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Does this mention ask for a code change (→ branch/diff/PR flow), or is it
|
|
124
|
+
* chat (→ a conversational reply)? Greetings and questions are chat even when
|
|
125
|
+
* they mention code; an action verb means work. Keeps casual talk in a
|
|
126
|
+
* repo-linked channel from spawning empty branches.
|
|
127
|
+
*/
|
|
128
|
+
export function looksLikeCodeTask(text) {
|
|
129
|
+
const t = String(text || "")
|
|
130
|
+
.toLowerCase()
|
|
131
|
+
.replace(/@[a-z0-9-]+/g, " ")
|
|
132
|
+
.trim();
|
|
133
|
+
if (!t) return false;
|
|
134
|
+
// Greetings / questions → chat (even if they name code things).
|
|
135
|
+
if (
|
|
136
|
+
/^(hi|hey|hello|yo|sup|gm|thanks|thank you|nice|cool|ok|okay|how|what|why|when|who|where|which|is\b|are\b|do you|does|did|could you (tell|explain|show|describe)|can you (tell|explain|show|describe))/.test(
|
|
137
|
+
t,
|
|
138
|
+
)
|
|
139
|
+
) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const verb =
|
|
143
|
+
/\b(fix|add|implement|refactor|change|update|create|remove|delete|rename|write|build|bump|migrate|wire|patch|optimi[sz]e|debug|revert|rebase|configure|improve|replace|extract|split|move|generate|scaffold|make|set up|hook up|adjust|tweak)\b/;
|
|
144
|
+
return verb.test(t);
|
|
145
|
+
}
|
|
146
|
+
|
|
122
147
|
/** owner/name from a GitHub remote URL (https or ssh), or null. */
|
|
123
148
|
export function normalizeRemote(url) {
|
|
124
149
|
const m = String(url || "")
|
|
@@ -145,12 +170,15 @@ async function respondConversationally({ message, channelId, tool, me, cfg }) {
|
|
|
145
170
|
`Conversation so far:\n${transcript}`;
|
|
146
171
|
|
|
147
172
|
const parts = cfg.codingCmd.split(" ").filter(Boolean);
|
|
173
|
+
console.log(` chat → running \`${cfg.codingCmd}\`…`);
|
|
148
174
|
const run = spawnSync(parts[0], [...parts.slice(1), prompt], {
|
|
149
175
|
encoding: "utf8",
|
|
150
176
|
timeout: cfg.runTimeoutMs,
|
|
151
177
|
maxBuffer: 50 * 1024 * 1024,
|
|
152
178
|
});
|
|
153
179
|
const reply = (run.stdout || "").trim();
|
|
180
|
+
if (run.error) console.log(` ! ${parts[0]} failed to start: ${run.error.message}`);
|
|
181
|
+
console.log(` chat → ${reply ? `replied (${reply.length} chars)` : "no output"}; posting`);
|
|
154
182
|
await tool("post_message", {
|
|
155
183
|
channelId,
|
|
156
184
|
body: reply || `(my CLI returned nothing — is \`${cfg.codingCmd}\` installed and on PATH?)`,
|
|
@@ -164,9 +192,9 @@ export async function handleTask({ message, channelId, tool, me }, cfg, depsOver
|
|
|
164
192
|
|
|
165
193
|
const { links = [] } = await tool("get_links", { channelId }).catch(() => ({ links: [] }));
|
|
166
194
|
const repoLink = links.find((l) => l.repo_full_name);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
195
|
+
// Chat (no repo linked, OR a greeting/question even in a repo channel) →
|
|
196
|
+
// reply via the CLI. Only an actual code request runs the branch/diff/PR flow.
|
|
197
|
+
if (!repoLink || !looksLikeCodeTask(message.body)) {
|
|
170
198
|
await respondConversationally({ message, channelId, tool, me, cfg });
|
|
171
199
|
return { status: "chat" };
|
|
172
200
|
}
|
|
@@ -223,15 +251,21 @@ export async function handleTask({ message, channelId, tool, me }, cfg, depsOver
|
|
|
223
251
|
|
|
224
252
|
const parts = cfg.codingCmd.split(" ").filter(Boolean);
|
|
225
253
|
const proposeRound = async (promptText) => {
|
|
254
|
+
console.log(` code → running \`${cfg.codingCmd}\` in ${repoPath}…`);
|
|
226
255
|
const run = spawnSync(parts[0], [...parts.slice(1), promptText], {
|
|
227
256
|
cwd: repoPath,
|
|
228
257
|
encoding: "utf8",
|
|
229
258
|
timeout: cfg.runTimeoutMs,
|
|
230
259
|
maxBuffer: 50 * 1024 * 1024,
|
|
231
260
|
});
|
|
261
|
+
if (run.error) console.log(` ! ${parts[0]} failed to start: ${run.error.message}`);
|
|
232
262
|
git(repoPath, ["add", "-A"]);
|
|
233
263
|
const diff = git(repoPath, ["diff", "--cached"]).stdout || "";
|
|
234
|
-
if (!diff.trim())
|
|
264
|
+
if (!diff.trim()) {
|
|
265
|
+
console.log(" code → no changes produced");
|
|
266
|
+
return { empty: true };
|
|
267
|
+
}
|
|
268
|
+
console.log(" code → diff captured; posting proposal");
|
|
235
269
|
const stat = parseShortstat(git(repoPath, ["diff", "--cached", "--shortstat"]).stdout);
|
|
236
270
|
const { text: diffText, truncated, omittedLines } = truncateDiff(diff);
|
|
237
271
|
const report = buildProposalReport({
|