jackherd 1.15.1 → 1.16.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 (54) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +22 -19
  3. package/dist/{chunk-V4HOLGFP.js → chunk-4LUFR7RC.js} +19 -1
  4. package/dist/config.d.ts +47 -2
  5. package/dist/config.js +65 -4
  6. package/dist/plan-format.js +3 -19
  7. package/herdr-plugin/herdr-plugin.toml +1 -1
  8. package/install.sh +49 -4
  9. package/kit/hooks/hooks/merge-guard/decision.ts +17 -2
  10. package/kit/workflow/skills/follow/SKILL.md +18 -10
  11. package/kit/workflow/skills/orchestrate/SKILL.md +9 -6
  12. package/package.json +2 -1
  13. package/recipes/README.md +3 -2
  14. package/recipes/google-calendar.md +30 -0
  15. package/recipes/sources/google-calendar-today.ts +32 -0
  16. package/recipes/sources/google-calendar.ts +368 -0
  17. package/recipes/sources.md +15 -3
  18. package/schemas/jackherd.schema.json +12 -0
  19. package/schemas/sources.schema.json +141 -0
  20. package/src/check/skill-budget-defaults.ts +11 -4
  21. package/src/cli.ts +1 -0
  22. package/src/config/json-schema.ts +7 -3
  23. package/src/config/location.ts +7 -0
  24. package/src/config/personal-sources.ts +83 -0
  25. package/src/config/schema.ts +29 -0
  26. package/src/plans/herd-claim.ts +9 -31
  27. package/src/plans/layout.ts +135 -18
  28. package/src/plans/live.ts +54 -7
  29. package/src/plans/row-actions.ts +17 -10
  30. package/src/plans/settings.ts +20 -7
  31. package/src/plans/snapshot.ts +34 -22
  32. package/src/plans/source-rows.ts +35 -2
  33. package/src/secrets/command.ts +163 -0
  34. package/src/secrets/environment.ts +45 -0
  35. package/src/secrets/hidden-input.ts +51 -0
  36. package/src/secrets/listing.ts +63 -0
  37. package/src/secrets/options.ts +45 -0
  38. package/src/secrets/store.ts +74 -0
  39. package/src/shared/process-info.ts +89 -0
  40. package/src/sync/command-context.ts +26 -4
  41. package/src/sync/render.ts +9 -4
  42. package/src/watch/command.ts +44 -12
  43. package/src/watch/lane-report.ts +2 -1
  44. package/src/watch/plan-badges-read.ts +28 -10
  45. package/src/watch/source-dry-run-command.ts +23 -12
  46. package/src/watch/source-dry-run.ts +20 -3
  47. package/src/watch/source-reload.ts +100 -0
  48. package/src/watch/source-run.ts +63 -7
  49. package/src/watch/source-tickets.ts +15 -0
  50. package/src/watch/sources.ts +21 -0
  51. package/src/watch/texts.ts +1 -1
  52. package/src/watch/watch-events.ts +9 -0
  53. package/src/watch/watched-source.ts +39 -0
  54. package/src/watch/watcher-lock.ts +8 -1
@@ -0,0 +1,39 @@
1
+ import { TONE_NAMES, type ToneName } from '../config/tones'
2
+ import { isRecord } from '../shared/is-record'
3
+ import { SOURCE_SHOW, type SourceSettings, type SourceShow } from './sources'
4
+
5
+ /**
6
+ * What the herd needs of a source, as the watcher writes it into its state: its name, the `label` and `tone` it is
7
+ * drawn with, whether its entries are tickets, its lane and how it shows its entries; null where the config sets none.
8
+ */
9
+ export type WatchedSource = {
10
+ name: string
11
+ label: string | null
12
+ tone: ToneName | null
13
+ ticket: boolean
14
+ lane: string | null
15
+ show: SourceShow | null
16
+ }
17
+
18
+ export const watchedSourceOf = ({ name, label, tone, ticket, lane, show }: SourceSettings): WatchedSource => ({
19
+ name,
20
+ label: label ?? null,
21
+ tone: tone ?? null,
22
+ ticket: ticket === true,
23
+ lane: lane ?? null,
24
+ show: show ?? null,
25
+ })
26
+
27
+ const isTextOrNull = (value: unknown): boolean => value === null || typeof value === 'string'
28
+
29
+ const isOneOfOrNull = (values: readonly string[], value: unknown): boolean =>
30
+ value === null || (typeof value === 'string' && values.includes(value))
31
+
32
+ export const isWatchedSource = (value: unknown): value is WatchedSource =>
33
+ isRecord(value) &&
34
+ typeof value.name === 'string' &&
35
+ isTextOrNull(value.label) &&
36
+ isOneOfOrNull(TONE_NAMES, value.tone) &&
37
+ typeof value.ticket === 'boolean' &&
38
+ isTextOrNull(value.lane) &&
39
+ isOneOfOrNull(Object.values(SOURCE_SHOW), value.show)
@@ -85,6 +85,9 @@ export const repositoryKey = (root: string): string => {
85
85
  /** How long the herd trusts a command line `ps` gave for a process ID that is still alive. */
86
86
  const PS_CACHE_MS = 30_000
87
87
 
88
+ /** Longest a `ps` call may take before it counts as no answer. */
89
+ const PS_TIMEOUT_MS = 2000
90
+
88
91
  /** The command line from `/proc/<pid>/cmdline`, its arguments blank-separated; null where there is no `/proc` or no such process. */
89
92
  export const procCommandLine: CommandLineOf = (pid) => {
90
93
  try {
@@ -95,7 +98,11 @@ export const procCommandLine: CommandLineOf = (pid) => {
95
98
  }
96
99
 
97
100
  const psCommandLine: CommandLineOf = (pid) => {
98
- const result = Bun.spawnSync(['ps', '-o', 'command=', '-p', String(pid)], { stdout: 'pipe', stderr: 'ignore' })
101
+ const result = Bun.spawnSync(['ps', '-o', 'command=', '-p', String(pid)], {
102
+ stdout: 'pipe',
103
+ stderr: 'ignore',
104
+ timeout: PS_TIMEOUT_MS,
105
+ })
99
106
  return result.exitCode === 0 ? result.stdout.toString().trim() || null : null
100
107
  }
101
108