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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. 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.2",
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
- if (!repoLink) {
168
- // No repo here respond conversationally by running YOUR CLI. This is the
169
- // agent genuinely answering in chat (the CLI is the brain), not a stub.
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()) return { empty: true };
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({