@raidiant/notifai 0.5.0 → 1.0.0

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 (74) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +11 -4
  3. package/dist/claude-wake.d.ts +101 -0
  4. package/dist/claude-wake.js +397 -0
  5. package/dist/claude-wake.js.map +1 -0
  6. package/dist/codex-wake.d.ts +84 -0
  7. package/dist/codex-wake.js +238 -0
  8. package/dist/codex-wake.js.map +1 -0
  9. package/dist/commands.d.ts +31 -11
  10. package/dist/commands.js +679 -232
  11. package/dist/commands.js.map +1 -1
  12. package/dist/config-schema.d.ts +3 -3
  13. package/dist/config-schema.js +11 -49
  14. package/dist/config-schema.js.map +1 -1
  15. package/dist/config.d.ts +23 -45
  16. package/dist/config.js +61 -39
  17. package/dist/config.js.map +1 -1
  18. package/dist/harnesses.d.ts +4 -13
  19. package/dist/harnesses.js +12 -17
  20. package/dist/harnesses.js.map +1 -1
  21. package/dist/hook-adapter.d.ts +10 -1
  22. package/dist/hook-adapter.js +79 -10
  23. package/dist/hook-adapter.js.map +1 -1
  24. package/dist/hooks.d.ts +119 -65
  25. package/dist/hooks.js +330 -285
  26. package/dist/hooks.js.map +1 -1
  27. package/dist/install-hooks.d.ts +76 -24
  28. package/dist/install-hooks.js +301 -125
  29. package/dist/install-hooks.js.map +1 -1
  30. package/dist/interactive.d.ts +32 -1
  31. package/dist/interactive.js +165 -51
  32. package/dist/interactive.js.map +1 -1
  33. package/dist/main.js +14 -21
  34. package/dist/main.js.map +1 -1
  35. package/dist/native-skills.d.ts +1 -1
  36. package/dist/native-skills.js +23 -58
  37. package/dist/native-skills.js.map +1 -1
  38. package/dist/pack-feedback-slice.d.ts +35 -0
  39. package/dist/pack-feedback-slice.js +126 -0
  40. package/dist/pack-feedback-slice.js.map +1 -0
  41. package/dist/readiness.d.ts +20 -0
  42. package/dist/readiness.js +24 -0
  43. package/dist/readiness.js.map +1 -1
  44. package/dist/release.d.ts +31 -0
  45. package/dist/release.js +52 -0
  46. package/dist/release.js.map +1 -0
  47. package/dist/ui/theme.d.ts +1 -1
  48. package/package.json +11 -10
  49. package/src/commands.test.ts +386 -36
  50. package/src/commands.ts +473 -182
  51. package/src/config-schema.ts +2 -17
  52. package/src/config.test.ts +29 -4
  53. package/src/config.ts +67 -11
  54. package/src/harnesses.ts +7 -0
  55. package/src/hook-adapter.test.ts +43 -1
  56. package/src/hook-adapter.ts +83 -11
  57. package/src/hooks.test.ts +4 -5
  58. package/src/install-hooks.test.ts +154 -2
  59. package/src/install-hooks.ts +231 -44
  60. package/src/interactive.test.ts +338 -0
  61. package/src/interactive.ts +221 -49
  62. package/src/main.ts +14 -20
  63. package/src/native-skills.test.ts +75 -0
  64. package/src/native-skills.ts +31 -75
  65. package/src/pack-feedback-slice.test.ts +212 -0
  66. package/src/pack-feedback-slice.ts +187 -0
  67. package/src/readiness.test.ts +25 -0
  68. package/src/readiness.ts +38 -0
  69. package/src/release.ts +51 -0
  70. package/src/skill.test.ts +43 -0
  71. package/src/ui/config-view.test.ts +5 -4
  72. package/dist/idle.d.ts +0 -16
  73. package/dist/idle.js +0 -44
  74. package/dist/idle.js.map +0 -1
package/src/skill.test.ts CHANGED
@@ -20,6 +20,33 @@ describe('Notifai agent skill', () => {
20
20
  expect(skill).toContain('| question (`--reply`) | `attention` | `active` |')
21
21
  })
22
22
 
23
+ it('makes the agent the setup wizard', () => {
24
+ expect(skill.indexOf('## Set Notifai up')).toBeGreaterThan(skill.indexOf('## When to notify'))
25
+ expect(skill.indexOf('## Set Notifai up')).toBeLessThan(skill.indexOf('## Compose and send'))
26
+ expect(skill).toContain('Never tell the user to run')
27
+ expect(skill).toContain('notifai init')
28
+ expect(skill).toContain('notifai hooks install')
29
+ expect(skill).toContain('notifai doctor')
30
+ expect(skill).toContain('notifai login')
31
+ expect(skill).toContain('Ask the user only for decisions a process cannot make')
32
+ expect(skill).not.toContain('relay the exact action')
33
+ expect(harnessReference).toContain('install hooks yourself')
34
+ expect(harnessReference).toContain('run `notifai login` yourself')
35
+ expect(harnessReference).not.toContain('the user must run `notifai login`')
36
+ expect(harnessReference).not.toContain('do not install them without being asked')
37
+ })
38
+
39
+ it('treats --sound and --level as overrides, not completeness', () => {
40
+ expect(skill).toMatch(/Never pass `--sound` or `--level` unless/)
41
+ const examples = [...skill.matchAll(/```(?:bash)?\n([\s\S]*?)```/g)].map((match) => match[1])
42
+ const sendExamples = examples.filter((example) => example.includes('notifai send'))
43
+ expect(sendExamples.length).toBeGreaterThan(0)
44
+ for (const example of sendExamples) {
45
+ expect(example).not.toMatch(/(^|\s)--sound\b/)
46
+ expect(example).not.toMatch(/(^|\s)--level\b/)
47
+ }
48
+ })
49
+
23
50
  it('documents reply lifecycle, routing, session minting, and doctor exits', () => {
24
51
  for (const required of [
25
52
  '--reply-choice',
@@ -57,6 +84,22 @@ describe('Notifai agent skill', () => {
57
84
  expect(askGuidance).toMatch(/without asking the user to\s+confirm again/)
58
85
  })
59
86
 
87
+ it('teaches agents to report config JSON fields instead of paraphrasing defaults', () => {
88
+ expect(skill).toContain('notifai config show --json')
89
+ expect(skill).toContain('{ value, source, summary }')
90
+ expect(skill).toContain('Do not paraphrase')
91
+ expect(skill).not.toContain('config.local.toml')
92
+ expect(skill).toContain('outside the repository')
93
+ })
94
+
95
+ it('mentions npx only as a pinned alternative to a real install', () => {
96
+ expect(skill).toContain('npm install -g @raidiant/notifai')
97
+ expect(skill.indexOf('npm install -g @raidiant/notifai')).toBeLessThan(
98
+ skill.indexOf('npx --yes @raidiant/notifai@'),
99
+ )
100
+ expect(skill).toContain('do not present npx as the default')
101
+ })
102
+
60
103
  it('tells the agent where to look when something did not happen', () => {
61
104
  // The log's whole value is diagnostic, so the skill has to name the case
62
105
  // that produces it — a question that was registered and never travelled.
@@ -18,14 +18,14 @@ describe('renderConfigPlain', () => {
18
18
  // an audience that cannot see the styling.
19
19
  const lines = renderConfigPlain(config, false)
20
20
  expect(lines).toHaveLength(CONFIG_KEYS.length)
21
- expect(lines[0]).toBe(`base_url = ${JSON.stringify(config.base_url.value)}`)
21
+ expect(lines[0]).toBe(`wait_seconds = ${JSON.stringify(config.wait_seconds.value)}`)
22
22
  for (const line of lines) expect(line).toMatch(/^[a-z_]+ = /)
23
23
  })
24
24
 
25
25
  it('appends the raw source tag under --explain', () => {
26
26
  const lines = renderConfigPlain(config, true)
27
27
  expect(lines[0]).toBe(
28
- `base_url = ${JSON.stringify(config.base_url.value)} [${config.base_url.source}]`,
28
+ `wait_seconds = ${JSON.stringify(config.wait_seconds.value)} [${config.wait_seconds.source}]`,
29
29
  )
30
30
  })
31
31
 
@@ -50,8 +50,9 @@ describe('renderConfigList', () => {
50
50
  })
51
51
 
52
52
  it('reveals advanced keys only when asked', () => {
53
- expect(renderConfigList(config).join('\n')).not.toContain('base_url')
54
- expect(renderConfigList(config, { showAdvanced: true }).join('\n')).toContain('base_url')
53
+ expect(renderConfigList(config).join('\n')).not.toContain('collapse_key')
54
+ expect(renderConfigList(config, { showAdvanced: true }).join('\n')).toContain('collapse_key')
55
+ expect(renderConfigList(config, { showAdvanced: true }).join('\n')).not.toContain('base_url')
55
56
  })
56
57
 
57
58
  it('fits the terminal, so no row wraps into the next', () => {
package/dist/idle.d.ts DELETED
@@ -1,16 +0,0 @@
1
- /**
2
- * Seconds since the user last touched the keyboard or mouse, or null when this
3
- * machine cannot say.
4
- *
5
- * The hooks need to know whether the user is at their desk, and the only signal
6
- * they had was silence since the last prompt — which includes the agent's own
7
- * turn, so a user watching a long build was indistinguishable from a user who
8
- * had left. This is the corroborating signal.
9
- *
10
- * null is a first-class answer, not a failure: on a machine with no idle source
11
- * the caller keeps its previous behaviour rather than guessing. That is why
12
- * every error path here returns null instead of throwing.
13
- */
14
- export declare function readIdleSeconds(platform?: NodeJS.Platform): number | null;
15
- /** Exported for tests: HIDIdleTime is nanoseconds since the last HID event. */
16
- export declare function parseIdleSeconds(ioregOutput: string): number | null;
package/dist/idle.js DELETED
@@ -1,44 +0,0 @@
1
- import { execFileSync } from 'node:child_process';
2
- /**
3
- * Seconds since the user last touched the keyboard or mouse, or null when this
4
- * machine cannot say.
5
- *
6
- * The hooks need to know whether the user is at their desk, and the only signal
7
- * they had was silence since the last prompt — which includes the agent's own
8
- * turn, so a user watching a long build was indistinguishable from a user who
9
- * had left. This is the corroborating signal.
10
- *
11
- * null is a first-class answer, not a failure: on a machine with no idle source
12
- * the caller keeps its previous behaviour rather than guessing. That is why
13
- * every error path here returns null instead of throwing.
14
- */
15
- export function readIdleSeconds(platform = process.platform) {
16
- if (platform !== 'darwin')
17
- return null;
18
- try {
19
- // -r -k narrows the dump to the one node carrying the key (~5KB), so this
20
- // stays a ~20ms call on the hot path of every Stop hook.
21
- const out = execFileSync('ioreg', ['-c', 'IOHIDSystem', '-r', '-k', 'HIDIdleTime'], {
22
- encoding: 'utf8',
23
- timeout: 2000,
24
- maxBuffer: 1_000_000,
25
- stdio: ['ignore', 'pipe', 'ignore'],
26
- });
27
- return parseIdleSeconds(out);
28
- }
29
- catch {
30
- // Missing binary, sandbox denial, timeout — all mean "no signal".
31
- return null;
32
- }
33
- }
34
- /** Exported for tests: HIDIdleTime is nanoseconds since the last HID event. */
35
- export function parseIdleSeconds(ioregOutput) {
36
- const match = /"HIDIdleTime"\s*=\s*(\d+)/.exec(ioregOutput);
37
- if (match?.[1] === undefined)
38
- return null;
39
- const nanos = Number(match[1]);
40
- if (!Number.isFinite(nanos) || nanos < 0)
41
- return null;
42
- return nanos / 1e9;
43
- }
44
- //# sourceMappingURL=idle.js.map
package/dist/idle.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"idle.js","sourceRoot":"","sources":["../src/idle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,WAA4B,OAAO,CAAC,QAAQ;IAC1E,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACtC,IAAI,CAAC;QACH,0EAA0E;QAC1E,yDAAyD;QACzD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE;YAClF,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,SAAS;YACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAA;QACF,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,MAAM,KAAK,GAAG,2BAA2B,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3D,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACrD,OAAO,KAAK,GAAG,GAAG,CAAA;AACpB,CAAC"}