litmus-cli 1.4.97 → 1.4.98
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/README.md +15 -0
- package/dist/lib/ai-tracking.d.ts +4 -0
- package/dist/lib/ai-tracking.d.ts.map +1 -1
- package/dist/lib/ai-tracking.js +68 -0
- package/dist/lib/ai-tracking.js.map +1 -1
- package/dist/lib/capture-notice-ping.d.ts +7 -27
- package/dist/lib/capture-notice-ping.d.ts.map +1 -1
- package/dist/lib/capture-notice-ping.js +10 -42
- package/dist/lib/capture-notice-ping.js.map +1 -1
- package/dist/lib/capture-nudge.d.ts +5 -7
- package/dist/lib/capture-nudge.d.ts.map +1 -1
- package/dist/lib/capture-nudge.js +19 -35
- package/dist/lib/capture-nudge.js.map +1 -1
- package/dist/lib/mac-notifier.d.ts +4 -0
- package/dist/lib/mac-notifier.d.ts.map +1 -0
- package/dist/lib/mac-notifier.js +72 -0
- package/dist/lib/mac-notifier.js.map +1 -0
- package/dist/lib/notify.d.ts +4 -5
- package/dist/lib/notify.d.ts.map +1 -1
- package/dist/lib/notify.js +24 -25
- package/dist/lib/notify.js.map +1 -1
- package/dist/lib/watcher.js +20 -24
- package/dist/lib/watcher.js.map +1 -1
- package/dist/notification-assets/litmus.icns +0 -0
- package/dist/notification-assets/litmus.png +0 -0
- package/dist/notification-assets/notifier.js +12 -0
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const NUDGE_TRIGGER_TOOLS: readonly ["codex", "copilot", "claude_code"];
|
|
2
2
|
export type NudgeTriggerTool = (typeof NUDGE_TRIGGER_TOOLS)[number];
|
|
3
|
+
export type NudgeSetupAction = "approve_codex" | "trust_copilot" | "repair";
|
|
3
4
|
export declare const NUDGE_STATE_FILE_NAME = "nudge-state.json";
|
|
4
5
|
export interface NudgeState {
|
|
5
6
|
/** Consecutive env scans each trigger tool has been present on. */
|
|
@@ -17,6 +18,7 @@ export type NudgeAction = {
|
|
|
17
18
|
} | {
|
|
18
19
|
action: "nudge";
|
|
19
20
|
tool: NudgeTriggerTool;
|
|
21
|
+
setupAction: NudgeSetupAction;
|
|
20
22
|
}
|
|
21
23
|
/** Capture just started working — remove the notice file, latch off. */
|
|
22
24
|
| {
|
|
@@ -33,8 +35,9 @@ export type NudgeDecision = NudgeAction & {
|
|
|
33
35
|
* @param promptCount ai_prompt + ai_response events captured so far
|
|
34
36
|
* @param now epoch ms
|
|
35
37
|
* @param msRemaining ms to the effective deadline, null when none configured
|
|
38
|
+
* @param setupIssues confirmed, actionable faults; absent/unknown stays quiet
|
|
36
39
|
*/
|
|
37
|
-
export declare function decideNudge(state: NudgeState, scanTools: string[], promptCount: number, now: number, msRemaining: number | null): NudgeDecision;
|
|
40
|
+
export declare function decideNudge(state: NudgeState, scanTools: string[], promptCount: number, now: number, msRemaining: number | null, setupIssues?: Partial<Record<NudgeTriggerTool, NudgeSetupAction>>): NudgeDecision;
|
|
38
41
|
/** Tolerant load: anything unreadable or malformed means a fresh state. */
|
|
39
42
|
export declare function loadNudgeState(projectDir: string): NudgeState;
|
|
40
43
|
export declare function saveNudgeState(projectDir: string, state: NudgeState): void;
|
|
@@ -89,12 +92,7 @@ export declare function removeAiNoticeFile(projectDir: string): boolean;
|
|
|
89
92
|
* (zip, status) omit it; a missing file is then simply "not ours".
|
|
90
93
|
*/
|
|
91
94
|
export declare function isCliNoticeArtifact(projectDir: string, relPath: string, lastKnown?: Map<string, boolean>): boolean;
|
|
92
|
-
|
|
93
|
-
* @param withDetailsPointer false when the notice file could not be written
|
|
94
|
-
* (a candidate-owned file occupies the path) — pointing at a file that
|
|
95
|
-
* carries THEIR content would misdirect.
|
|
96
|
-
*/
|
|
97
|
-
export declare function nudgeNotificationCopy(tool: NudgeTriggerTool, withDetailsPointer?: boolean): {
|
|
95
|
+
export declare function nudgeNotificationCopy(action: NudgeSetupAction): {
|
|
98
96
|
title: string;
|
|
99
97
|
message: string;
|
|
100
98
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture-nudge.d.ts","sourceRoot":"","sources":["../../src/lib/capture-nudge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"capture-nudge.d.ts","sourceRoot":"","sources":["../../src/lib/capture-nudge.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,11 +4,9 @@ import { assessmentActivityLogs } from "./assessment-activity.js";
|
|
|
4
4
|
* (watcher.ts is a spawned script, so logic living there can't be imported
|
|
5
5
|
* by tests; same split as detection.ts).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* candidate whose AI work is invisibly not being recorded. The nudge is
|
|
11
|
-
* CANDIDATE-FACING ONLY: a desktop notification plus a notice file at the
|
|
7
|
+
* A running tool is nudged only when its configuration identifies a concrete
|
|
8
|
+
* setup action. Missing prompts alone are not a setup diagnosis. The nudge is
|
|
9
|
+
* CANDIDATE-FACING ONLY; no new notice file is written at the
|
|
12
10
|
* assessment root. It emits NO activity event — the grader renders every
|
|
13
11
|
* non-response activity event verbatim in the judge-visible timeline
|
|
14
12
|
* (substrate_sections.py) and merges the in-zip activity.jsonl, so anything
|
|
@@ -60,8 +58,9 @@ export function initialNudgeState() {
|
|
|
60
58
|
* @param promptCount ai_prompt + ai_response events captured so far
|
|
61
59
|
* @param now epoch ms
|
|
62
60
|
* @param msRemaining ms to the effective deadline, null when none configured
|
|
61
|
+
* @param setupIssues confirmed, actionable faults; absent/unknown stays quiet
|
|
63
62
|
*/
|
|
64
|
-
export function decideNudge(state, scanTools, promptCount, now, msRemaining) {
|
|
63
|
+
export function decideNudge(state, scanTools, promptCount, now, msRemaining, setupIssues = {}) {
|
|
65
64
|
if (state.latched)
|
|
66
65
|
return { action: "none", state };
|
|
67
66
|
// Capture confirmed: latch off permanently and signal notice removal.
|
|
@@ -73,7 +72,7 @@ export function decideNudge(state, scanTools, promptCount, now, msRemaining) {
|
|
|
73
72
|
const present = new Set(scanTools);
|
|
74
73
|
const consecutive = {};
|
|
75
74
|
for (const tool of NUDGE_TRIGGER_TOOLS) {
|
|
76
|
-
consecutive[tool] = present.has(tool) ? (state.consecutive[tool] ?? 0) + 1 : 0;
|
|
75
|
+
consecutive[tool] = present.has(tool) && setupIssues[tool] ? (state.consecutive[tool] ?? 0) + 1 : 0;
|
|
77
76
|
}
|
|
78
77
|
const next = { ...state, consecutive };
|
|
79
78
|
// First trigger tool (in copy-priority order) that has been present long
|
|
@@ -92,6 +91,7 @@ export function decideNudge(state, scanTools, promptCount, now, msRemaining) {
|
|
|
92
91
|
return {
|
|
93
92
|
action: "nudge",
|
|
94
93
|
tool,
|
|
94
|
+
setupAction: setupIssues[tool],
|
|
95
95
|
state: { ...next, nudgeCount: next.nudgeCount + 1, lastNudgeAt: now },
|
|
96
96
|
};
|
|
97
97
|
}
|
|
@@ -315,34 +315,18 @@ export function isCliNoticeArtifact(projectDir, relPath, lastKnown) {
|
|
|
315
315
|
return lastKnown?.get(relPath) ?? false;
|
|
316
316
|
}
|
|
317
317
|
// ── Notification copy ─────────────────────────────────────────────
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
let message;
|
|
331
|
-
switch (tool) {
|
|
332
|
-
case "codex":
|
|
333
|
-
message =
|
|
334
|
-
`No AI prompts captured yet. If you're using Codex, restart it (or trust the Litmus hooks under /hooks) so your AI work is recorded for review by the hiring company, then run "litmus doctor" to recover earlier prompts.`;
|
|
335
|
-
break;
|
|
336
|
-
case "copilot":
|
|
337
|
-
message =
|
|
338
|
-
`No AI prompts captured yet. If you're using the Copilot CLI, restart it (or answer yes to its folder-trust prompt) so your AI work is recorded for review by the hiring company, then run "litmus doctor" to recover earlier prompts.`;
|
|
339
|
-
break;
|
|
340
|
-
default:
|
|
341
|
-
message =
|
|
342
|
-
'No AI prompts captured yet. Run "litmus doctor" in your assessment folder to check AI tracking.';
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
return { title: "Litmus", message: withDetailsPointer ? `${message} ${NOTICE_POINTER}` : message };
|
|
318
|
+
// One action, selected from a confirmed setup issue. No capture-status
|
|
319
|
+
// disclosure or assessment-file pointer in candidate notifications.
|
|
320
|
+
export function nudgeNotificationCopy(action) {
|
|
321
|
+
if (action === "approve_codex")
|
|
322
|
+
return {
|
|
323
|
+
title: "Enable Litmus hooks", message: "Open /hooks in Codex and approve the Litmus hooks.",
|
|
324
|
+
};
|
|
325
|
+
if (action === "trust_copilot")
|
|
326
|
+
return {
|
|
327
|
+
title: "Approve folder access", message: "Accept the folder-trust prompt in Copilot CLI.",
|
|
328
|
+
};
|
|
329
|
+
return { title: "Finish setup", message: "Run litmus doctor in your assessment folder." };
|
|
346
330
|
}
|
|
347
331
|
/** Incremental count across both capture lanes, with independent byte cursors. */
|
|
348
332
|
export function scanAssessmentCapture(projectDir, token, cursors) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture-nudge.js","sourceRoot":"","sources":["../../src/lib/capture-nudge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE
|
|
1
|
+
{"version":3,"file":"capture-nudge.js","sourceRoot":"","sources":["../../src/lib/capture-nudge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mac-notifier.d.ts","sourceRoot":"","sources":["../../src/lib/mac-notifier.ts"],"names":[],"mappings":"AAgBA;8EAC8E;AAC9E,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAGpD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { cp, mkdir, mkdtemp, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
const assets = fileURLToPath(new URL("../notification-assets/", import.meta.url));
|
|
8
|
+
let preparing;
|
|
9
|
+
function run(command, args) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
execFile(command, args, { timeout: 15000, windowsHide: true }, error => error ? reject(error) : resolve());
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/** Built once per asset revision using macOS's built-in applet compiler. No
|
|
15
|
+
* downloaded executables, development toolchain, or installer is required. */
|
|
16
|
+
export function prepareMacNotifier() {
|
|
17
|
+
preparing ?? (preparing = prepare().catch(error => { preparing = undefined; throw error; }));
|
|
18
|
+
return preparing;
|
|
19
|
+
}
|
|
20
|
+
async function prepare() {
|
|
21
|
+
const script = await readFile(path.join(assets, "notifier.js"));
|
|
22
|
+
const icon = await readFile(path.join(assets, "litmus.icns"));
|
|
23
|
+
const revision = createHash("sha256").update("bundle-v1").update(script).update(icon).digest("hex").slice(0, 16);
|
|
24
|
+
const root = path.join(os.homedir(), ".litmus", "notifications", revision);
|
|
25
|
+
const app = path.join(root, "Litmus.app");
|
|
26
|
+
const ready = path.join(app, "Contents", "Resources", ".litmus-ready");
|
|
27
|
+
try {
|
|
28
|
+
if ((await stat(ready)).isFile())
|
|
29
|
+
return app;
|
|
30
|
+
}
|
|
31
|
+
catch { /* first use */ }
|
|
32
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
33
|
+
const staging = await mkdtemp(path.join(root, ".build-"));
|
|
34
|
+
const stagedApp = path.join(staging, "Litmus.app");
|
|
35
|
+
try {
|
|
36
|
+
await run("/usr/bin/osacompile", ["-l", "JavaScript", "-o", stagedApp, path.join(assets, "notifier.js")]);
|
|
37
|
+
await writeFile(path.join(stagedApp, "Contents", "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
|
|
38
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
39
|
+
<plist version="1.0"><dict>
|
|
40
|
+
<key>CFBundleExecutable</key><string>applet</string>
|
|
41
|
+
<key>CFBundleIdentifier</key><string>com.litmushiring.notifications</string>
|
|
42
|
+
<key>CFBundleName</key><string>Litmus</string>
|
|
43
|
+
<key>CFBundleIconFile</key><string>litmus.icns</string>
|
|
44
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
45
|
+
<key>CFBundleVersion</key><string>1</string>
|
|
46
|
+
<key>LSUIElement</key><true/>
|
|
47
|
+
<key>OSAAppletShowStartupScreen</key><false/>
|
|
48
|
+
</dict></plist>`);
|
|
49
|
+
await cp(path.join(assets, "litmus.icns"), path.join(stagedApp, "Contents", "Resources", "litmus.icns"));
|
|
50
|
+
// Editing a compiled app invalidates its ad-hoc signature. Sign the final
|
|
51
|
+
// bundle, not the source applet, before launching it.
|
|
52
|
+
await writeFile(path.join(stagedApp, "Contents", "Resources", ".litmus-ready"), revision);
|
|
53
|
+
await run("/usr/bin/codesign", ["--force", "--sign", "-", stagedApp]);
|
|
54
|
+
try {
|
|
55
|
+
await rename(stagedApp, app);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Another watcher may have installed this exact revision first.
|
|
59
|
+
try {
|
|
60
|
+
await stat(ready);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return app;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
await rm(staging, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=mac-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mac-notifier.js","sourceRoot":"","sources":["../../src/lib/mac-notifier.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/notify.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Best-effort desktop notification (ENG-1857) — zero dependencies, silent
|
|
3
3
|
* failure. Used by the watcher to reach a candidate whose terminal (and
|
|
4
4
|
* tracker.log) they are not looking at. It must never throw and never block:
|
|
5
|
-
*
|
|
5
|
+
* notification sends use execFile with a 5s timeout, and a missing
|
|
6
6
|
* binary (no notify-send, locked-down PowerShell) is simply a no-op.
|
|
7
7
|
*/
|
|
8
8
|
import { execFile } from "child_process";
|
|
@@ -11,15 +11,14 @@ import { execFile } from "child_process";
|
|
|
11
11
|
* run. Pure — split out so tests can check construction (quote escaping)
|
|
12
12
|
* without spawning anything.
|
|
13
13
|
*/
|
|
14
|
-
export declare function buildNotifyCommand(platform: NodeJS.Platform, title: string, message: string): {
|
|
14
|
+
export declare function buildNotifyCommand(platform: NodeJS.Platform, title: string, message: string, macApp?: string): {
|
|
15
15
|
cmd: string;
|
|
16
16
|
args: string[];
|
|
17
17
|
} | null;
|
|
18
18
|
/**
|
|
19
19
|
* Show a desktop notification. Never throws, never blocks the caller;
|
|
20
20
|
* failure of any kind (missing binary, DE without a notification daemon,
|
|
21
|
-
* headless session) is silently ignored
|
|
22
|
-
* fallback surface.
|
|
21
|
+
* headless session) is silently ignored.
|
|
23
22
|
*/
|
|
24
23
|
export declare function desktopNotify(title: string, message: string, exec?: typeof execFile): void;
|
|
25
24
|
/**
|
|
@@ -27,5 +26,5 @@ export declare function desktopNotify(title: string, message: string, exec?: typ
|
|
|
27
26
|
* caller that retries an unsent notification (ENG-3307's capture notice). Never
|
|
28
27
|
* rejects; resolves false on a missing binary, a non-zero exit or the timeout.
|
|
29
28
|
*/
|
|
30
|
-
export declare function desktopNotifyResult(title: string, message: string, exec?: typeof execFile): Promise<boolean>;
|
|
29
|
+
export declare function desktopNotifyResult(title: string, message: string, exec?: typeof execFile, prepare?: () => Promise<string>): Promise<boolean>;
|
|
31
30
|
//# sourceMappingURL=notify.d.ts.map
|
package/dist/lib/notify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAWxC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA4BxC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,OAAO,QAAmB,GAC/B,IAAI,CAEN;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,OAAO,QAAmB,EAChC,OAAO,GAAE,MAAM,OAAO,CAAC,MAAM,CAAsB,GAClD,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
|
package/dist/lib/notify.js
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* Best-effort desktop notification (ENG-1857) — zero dependencies, silent
|
|
3
3
|
* failure. Used by the watcher to reach a candidate whose terminal (and
|
|
4
4
|
* tracker.log) they are not looking at. It must never throw and never block:
|
|
5
|
-
*
|
|
5
|
+
* notification sends use execFile with a 5s timeout, and a missing
|
|
6
6
|
* binary (no notify-send, locked-down PowerShell) is simply a no-op.
|
|
7
7
|
*/
|
|
8
8
|
import { execFile } from "child_process";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
10
|
+
import { prepareMacNotifier } from "./mac-notifier.js";
|
|
11
|
+
const notificationIcon = fileURLToPath(new URL("../notification-assets/litmus.png", import.meta.url));
|
|
13
12
|
/** PowerShell single-quoted string literal: single quotes doubled. */
|
|
14
13
|
function powerShellString(s) {
|
|
15
14
|
return `'${s.replace(/'/g, "''")}'`;
|
|
@@ -19,12 +18,11 @@ function powerShellString(s) {
|
|
|
19
18
|
* run. Pure — split out so tests can check construction (quote escaping)
|
|
20
19
|
* without spawning anything.
|
|
21
20
|
*/
|
|
22
|
-
export function buildNotifyCommand(platform, title, message) {
|
|
21
|
+
export function buildNotifyCommand(platform, title, message, macApp) {
|
|
23
22
|
if (platform === "darwin") {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
23
|
+
if (!macApp)
|
|
24
|
+
return null;
|
|
25
|
+
return { cmd: "open", args: ["-g", "-n", "-W", macApp, "--args", "--title", title, "--message", message] };
|
|
28
26
|
}
|
|
29
27
|
if (platform === "win32") {
|
|
30
28
|
// WinRT toast via PowerShell — present on every supported Windows, no
|
|
@@ -33,10 +31,12 @@ export function buildNotifyCommand(platform, title, message) {
|
|
|
33
31
|
// needed on top.
|
|
34
32
|
const script = [
|
|
35
33
|
"[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null",
|
|
36
|
-
"$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::
|
|
34
|
+
"$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02)",
|
|
37
35
|
"$texts = $template.GetElementsByTagName('text')",
|
|
38
36
|
`$texts.Item(0).AppendChild($template.CreateTextNode(${powerShellString(title)})) | Out-Null`,
|
|
39
37
|
`$texts.Item(1).AppendChild($template.CreateTextNode(${powerShellString(message)})) | Out-Null`,
|
|
38
|
+
"$image = $template.GetElementsByTagName('image').Item(0)",
|
|
39
|
+
`$image.SetAttribute('src', ${powerShellString(pathToFileURL(notificationIcon).href)})`,
|
|
40
40
|
"$toast = [Windows.UI.Notifications.ToastNotification]::new($template)",
|
|
41
41
|
`[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(${powerShellString("Litmus")}).Show($toast)`,
|
|
42
42
|
].join("; ");
|
|
@@ -44,36 +44,35 @@ export function buildNotifyCommand(platform, title, message) {
|
|
|
44
44
|
}
|
|
45
45
|
if (platform === "linux") {
|
|
46
46
|
// notify-send if present; execFile's ENOENT is swallowed below when not.
|
|
47
|
-
return { cmd: "notify-send", args: [title, message] };
|
|
47
|
+
return { cmd: "notify-send", args: ["--icon", notificationIcon, "--", title, message] };
|
|
48
48
|
}
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Show a desktop notification. Never throws, never blocks the caller;
|
|
53
53
|
* failure of any kind (missing binary, DE without a notification daemon,
|
|
54
|
-
* headless session) is silently ignored
|
|
55
|
-
* fallback surface.
|
|
54
|
+
* headless session) is silently ignored.
|
|
56
55
|
*/
|
|
57
56
|
export function desktopNotify(title, message, exec = execFile) {
|
|
58
|
-
|
|
59
|
-
const command = buildNotifyCommand(process.platform, title, message);
|
|
60
|
-
if (!command)
|
|
61
|
-
return;
|
|
62
|
-
const child = exec(command.cmd, command.args, { timeout: 5000, windowsHide: true }, () => { });
|
|
63
|
-
// Detach from the event loop so a slow notifier can't hold the watcher up.
|
|
64
|
-
child.unref?.();
|
|
65
|
-
}
|
|
66
|
-
catch { /* never let a notification failure surface */ }
|
|
57
|
+
void desktopNotifyResult(title, message, exec);
|
|
67
58
|
}
|
|
68
59
|
/**
|
|
69
60
|
* `desktopNotify`, reporting whether the notifier ran and exited cleanly. For a
|
|
70
61
|
* caller that retries an unsent notification (ENG-3307's capture notice). Never
|
|
71
62
|
* rejects; resolves false on a missing binary, a non-zero exit or the timeout.
|
|
72
63
|
*/
|
|
73
|
-
export function desktopNotifyResult(title, message, exec = execFile) {
|
|
64
|
+
export async function desktopNotifyResult(title, message, exec = execFile, prepare = prepareMacNotifier) {
|
|
65
|
+
let macApp;
|
|
66
|
+
try {
|
|
67
|
+
if (process.platform === "darwin")
|
|
68
|
+
macApp = await prepare();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
74
73
|
return new Promise((resolve) => {
|
|
75
74
|
try {
|
|
76
|
-
const command = buildNotifyCommand(process.platform, title, message);
|
|
75
|
+
const command = buildNotifyCommand(process.platform, title, message, macApp);
|
|
77
76
|
if (!command)
|
|
78
77
|
return resolve(false);
|
|
79
78
|
const child = exec(command.cmd, command.args, { timeout: 5000, windowsHide: true }, (error) => resolve(!error));
|
package/dist/lib/notify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/watcher.js
CHANGED
|
@@ -34,7 +34,7 @@ import { buildGitBundle, currentHead } from "./git-bundle.js";
|
|
|
34
34
|
import { fetchInitMetadata, ServerError } from "./api.js";
|
|
35
35
|
import { resolveBackendBase } from "./api-base.js";
|
|
36
36
|
import { ServerTimesSchema } from "./schemas.js";
|
|
37
|
-
import { detectInstalledHooks, addActiveAssessment, removeActiveAssessment } from "./ai-tracking.js";
|
|
37
|
+
import { detectInstalledHooks, notificationSetupAction, addActiveAssessment, removeActiveAssessment } from "./ai-tracking.js";
|
|
38
38
|
import { retireAiCaptureForEndedSession, sessionOverMarked } from "./session-over.js";
|
|
39
39
|
import { readActivityLogTail } from "./activity-tail.js";
|
|
40
40
|
import { setupAiTracking } from "../commands/init.js";
|
|
@@ -43,7 +43,7 @@ import { createKeepAliveGuard } from "./keepalive.js";
|
|
|
43
43
|
import { WATCHER_READY_GRACE_MS, WATCHER_BOOT_TIMEOUT_MS } from "./tracker.js";
|
|
44
44
|
import { getProcessListAsync, probeUnfilteredTasklist, getConnections, extractConnection, resolvePidNames } from "./platform.js";
|
|
45
45
|
import { detectTools, refineClaudeTool } from "./detection.js";
|
|
46
|
-
import { decideNudge, loadNudgeState, saveNudgeState, scanAssessmentCapture,
|
|
46
|
+
import { decideNudge, loadNudgeState, saveNudgeState, scanAssessmentCapture, NUDGE_TRIGGER_TOOLS, removeAiNoticeFile, nudgeNotificationCopy, isCliNoticeArtifact, AI_NOTICE_FILE_NAME, } from "./capture-nudge.js";
|
|
47
47
|
import { desktopNotify } from "./notify.js";
|
|
48
48
|
import { codexStoreInventory, readCodexDelivery } from "./codex-capture-health.js";
|
|
49
49
|
// ENG-1491: an uncaught throw no longer always kills the watcher — on a
|
|
@@ -1180,18 +1180,14 @@ function checkGitCommits() {
|
|
|
1180
1180
|
}
|
|
1181
1181
|
setInterval(checkGitCommits, 30 * 1000); // Every 30 seconds
|
|
1182
1182
|
// ── Capture nudge (ENG-1857) ─────────────────────────────────────
|
|
1183
|
-
//
|
|
1184
|
-
//
|
|
1185
|
-
//
|
|
1186
|
-
//
|
|
1187
|
-
// the assessment root, and stderr (-> .litmus/tracker.log, excluded from all
|
|
1188
|
-
// grader signals via substrate.py's _is_capture_artifact). It must emit NO
|
|
1189
|
-
// activity event — the grader renders every non-response activity event
|
|
1190
|
-
// verbatim in the judge-visible timeline and merges the in-zip
|
|
1191
|
-
// activity.jsonl, so an event here would leak straight to reviewers.
|
|
1183
|
+
// Ask for one action only when an active tool has a confirmed setup fault.
|
|
1184
|
+
// No conversations alone proves nothing. Candidate notifications emit no
|
|
1185
|
+
// activity event: the grader renders those in the judge-visible timeline.
|
|
1186
|
+
// Diagnostics stay on stderr (.litmus/tracker.log, excluded from grading).
|
|
1192
1187
|
//
|
|
1193
1188
|
// State persists in .litmus/nudge-state.json so the routine watcher respawns
|
|
1194
1189
|
// (litmus status/doctor revive watchers) don't re-nag from scratch.
|
|
1190
|
+
removeOwnedAiNotice(); // retire guidance left by older CLI versions
|
|
1195
1191
|
let nudgeState = loadNudgeState(projectDir);
|
|
1196
1192
|
// Incremental ai_prompt/ai_response counters over the folder and same-token
|
|
1197
1193
|
// supervisor logs; only appended bytes are read each cycle. A restart rescans
|
|
@@ -1218,24 +1214,24 @@ function runCaptureNudge(aiTools) {
|
|
|
1218
1214
|
}
|
|
1219
1215
|
const deadlineMs = litmusConfig ? getEffectiveDeadline(litmusConfig) : null;
|
|
1220
1216
|
const msRemaining = deadlineMs === null ? null : deadlineMs - Date.now();
|
|
1221
|
-
const
|
|
1217
|
+
const setupIssues = {};
|
|
1218
|
+
for (const tool of NUDGE_TRIGGER_TOOLS) {
|
|
1219
|
+
if (!aiTools.includes(tool))
|
|
1220
|
+
continue;
|
|
1221
|
+
const issue = notificationSetupAction(projectDir, tool);
|
|
1222
|
+
if (issue)
|
|
1223
|
+
setupIssues[tool] = issue;
|
|
1224
|
+
}
|
|
1225
|
+
const decision = decideNudge(nudgeState, aiTools, capturedAiCount, Date.now(), msRemaining, setupIssues);
|
|
1222
1226
|
nudgeState = decision.state;
|
|
1223
1227
|
saveNudgeState(projectDir, nudgeState);
|
|
1224
1228
|
if (decision.action === "nudge") {
|
|
1225
|
-
//
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const noticeWritten = writeAiNoticeFile(projectDir);
|
|
1229
|
-
if (noticeWritten) {
|
|
1230
|
-
isCliNoticeArtifact(projectDir, AI_NOTICE_FILE_NAME, noticeOwnership); // refresh ownership
|
|
1231
|
-
}
|
|
1232
|
-
else {
|
|
1233
|
-
process.stderr.write(`[watcher] ${AI_NOTICE_FILE_NAME} exists and is not ours; notification only\n`);
|
|
1234
|
-
}
|
|
1235
|
-
const copy = nudgeNotificationCopy(decision.tool, noticeWritten);
|
|
1229
|
+
// Retire old guidance without touching candidate edits.
|
|
1230
|
+
removeOwnedAiNotice();
|
|
1231
|
+
const copy = nudgeNotificationCopy(decision.setupAction);
|
|
1236
1232
|
desktopNotify(copy.title, copy.message);
|
|
1237
1233
|
// stderr only — this lands in tracker.log, never in activity.jsonl.
|
|
1238
|
-
process.stderr.write(`[watcher] capture nudge fired (${decision.tool}, nudge ${nudgeState.nudgeCount}):
|
|
1234
|
+
process.stderr.write(`[watcher] capture nudge fired (${decision.tool}, nudge ${nudgeState.nudgeCount}): confirmed setup issue ${decision.setupAction}\n`);
|
|
1239
1235
|
}
|
|
1240
1236
|
else if (decision.action === "remove_notice") {
|
|
1241
1237
|
removeOwnedAiNotice();
|