fix: narrow skills watcher ignores (#1074) (thanks @roshanasingh4)

This commit is contained in:
Peter Steinberger 2026-01-17 07:56:35 +00:00
parent 6aaa302be5
commit e13a332ac8
7 changed files with 11 additions and 8 deletions

View File

@ -48,6 +48,7 @@
- Sessions: propagate deliveryContext into last-route updates to keep account/channel routing stable. (#1058) - Sessions: propagate deliveryContext into last-route updates to keep account/channel routing stable. (#1058)
- Gateway: honor explicit delivery targets without implicit accountId fallback; preserve lastAccountId for implicit routing. - Gateway: honor explicit delivery targets without implicit accountId fallback; preserve lastAccountId for implicit routing.
- Gateway: avoid reusing last-to/accountId when the requested channel differs; sync deliveryContext with last route fields. - Gateway: avoid reusing last-to/accountId when the requested channel differs; sync deliveryContext with last route fields.
- Skills: narrow skills watcher ignores to `.git` so hidden workspaces still refresh. (#1074) — thanks @roshanasingh4.
- Repo: fix oxlint config filename and move ignore pattern into config. (#1064) — thanks @connorshea. - Repo: fix oxlint config filename and move ignore pattern into config. (#1064) — thanks @connorshea.
- Messages: `/stop` now hard-aborts queued followups and sub-agent runs; suppress zero-count stop notes. - Messages: `/stop` now hard-aborts queued followups and sub-agent runs; suppress zero-count stop notes.
- Messages: include sender labels for live group messages across channels, matching queued/history formatting. (#1059) - Messages: include sender labels for live group messages across channels, matching queued/history formatting. (#1059)

View File

@ -50,7 +50,7 @@ test("process submit sends CR for pty sessions", async () => {
const processTool = createProcessTool(); const processTool = createProcessTool();
const result = await execTool.execute("toolcall", { const result = await execTool.execute("toolcall", {
command: command:
"node -e \"process.stdin.on('data', d => { if (d.includes(13)) { process.stdout.write('submitted'); process.exit(0); } });\"", "node -e \"process.stdin.on('data', d => { if (d.includes(10) || d.includes(13)) { process.stdout.write('submitted'); process.exit(0); } });\"",
pty: true, pty: true,
background: true, background: true,
}); });

View File

@ -1,4 +1,5 @@
const DSR_PATTERN = /\x1b\[\??6n/g; const ESC = "\u001b";
const DSR_PATTERN = new RegExp(`${ESC}\\[\\??6n`, "g");
export function stripDsrRequests(input: string): { cleaned: string; requests: number } { export function stripDsrRequests(input: string): { cleaned: string; requests: number } {
let requests = 0; let requests = 0;

View File

@ -12,7 +12,7 @@ vi.mock("chokidar", () => {
}); });
describe("ensureSkillsWatcher", () => { describe("ensureSkillsWatcher", () => {
it("ignores node_modules and dist by default", async () => { it("ignores node_modules, dist, and .git by default", async () => {
const mod = await import("./refresh.js"); const mod = await import("./refresh.js");
mod.ensureSkillsWatcher({ workspaceDir: "/tmp/workspace" }); mod.ensureSkillsWatcher({ workspaceDir: "/tmp/workspace" });
@ -26,5 +26,6 @@ describe("ensureSkillsWatcher", () => {
); );
expect(ignored.some((re) => re.test("/tmp/workspace/skills/dist/index.js"))).toBe(true); expect(ignored.some((re) => re.test("/tmp/workspace/skills/dist/index.js"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/.git/config"))).toBe(true); expect(ignored.some((re) => re.test("/tmp/workspace/skills/.git/config"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/.hidden/skills/index.md"))).toBe(false);
}); });
}); });

View File

@ -128,7 +128,7 @@ export function ensureSkillsWatcher(params: { workspaceDir: string; config?: Cla
// Avoid FD exhaustion on macOS when a workspace contains huge trees. // Avoid FD exhaustion on macOS when a workspace contains huge trees.
// This watcher only needs to react to skill changes. // This watcher only needs to react to skill changes.
ignored: [ ignored: [
/(^|[\\/])\../, // dotfiles (includes .git) /(^|[\\/])\.git([\\/]|$)/,
/(^|[\\/])node_modules([\\/]|$)/, /(^|[\\/])node_modules([\\/]|$)/,
/(^|[\\/])dist([\\/]|$)/, /(^|[\\/])dist([\\/]|$)/,
], ],

View File

@ -677,15 +677,15 @@ export function registerHooksCli(program: Command): void {
for (const hookId of targets) { for (const hookId of targets) {
const record = installs[hookId]; const record = installs[hookId];
if (!record) { if (!record) {
defaultRuntime.log(chalk.yellow(`No install record for \"${hookId}\".`)); defaultRuntime.log(chalk.yellow(`No install record for "${hookId}".`));
continue; continue;
} }
if (record.source !== "npm") { if (record.source !== "npm") {
defaultRuntime.log(chalk.yellow(`Skipping \"${hookId}\" (source: ${record.source}).`)); defaultRuntime.log(chalk.yellow(`Skipping "${hookId}" (source: ${record.source}).`));
continue; continue;
} }
if (!record.spec) { if (!record.spec) {
defaultRuntime.log(chalk.yellow(`Skipping \"${hookId}\" (missing npm spec).`)); defaultRuntime.log(chalk.yellow(`Skipping "${hookId}" (missing npm spec).`));
continue; continue;
} }

View File

@ -102,7 +102,7 @@ export function normalizeAttachments(ctx: MsgContext): MediaAttachment[] {
export function resolveAttachmentKind( export function resolveAttachmentKind(
attachment: MediaAttachment, attachment: MediaAttachment,
): "image" | "audio" | "video" | "unknown" { ): "image" | "audio" | "video" | "document" | "unknown" {
const kind = kindFromMime(attachment.mime); const kind = kindFromMime(attachment.mime);
if (kind !== "unknown") return kind; if (kind !== "unknown") return kind;