@stablekernel/pi-background-run 0.5.0 → 0.6.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.
- package/README.md +452 -59
- package/extension/digestPresets.ts +316 -0
- package/extension/index.ts +2315 -478
- package/package.json +10 -3
- package/skill/digest-config/SKILL.md +117 -0
- package/skill/run-bg/SKILL.md +81 -24
- package/extension/index.test.ts +0 -2569
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stablekernel/pi-background-run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Run long shell commands detached in the background for pi; get woken on completion. Output lands in a file; context stays clean.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
6
9
|
"license": "MIT",
|
|
7
10
|
"publishConfig": {
|
|
8
11
|
"access": "public"
|
|
@@ -15,15 +18,18 @@
|
|
|
15
18
|
"./extension/index.ts"
|
|
16
19
|
],
|
|
17
20
|
"skills": [
|
|
18
|
-
"./skill/run-bg"
|
|
21
|
+
"./skill/run-bg",
|
|
22
|
+
"./skill/digest-config"
|
|
19
23
|
]
|
|
20
24
|
},
|
|
21
25
|
"scripts": {
|
|
22
26
|
"test": "bun test extension/index.test.ts",
|
|
23
|
-
"lint": "tsc --noEmit"
|
|
27
|
+
"lint": "tsc --noEmit",
|
|
28
|
+
"lint:pr-title": "bun scripts/check-conventional-commit.ts"
|
|
24
29
|
},
|
|
25
30
|
"files": [
|
|
26
31
|
"extension/",
|
|
32
|
+
"!extension/index.test.ts",
|
|
27
33
|
"skill/",
|
|
28
34
|
"README.md",
|
|
29
35
|
"LICENSE"
|
|
@@ -56,6 +62,7 @@
|
|
|
56
62
|
"devDependencies": {
|
|
57
63
|
"@earendil-works/pi-coding-agent": "*",
|
|
58
64
|
"@earendil-works/pi-tui": "*",
|
|
65
|
+
"@types/node": "^24.0.0",
|
|
59
66
|
"typebox": "^1.3.0",
|
|
60
67
|
"typescript": "^5.7.0"
|
|
61
68
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: digest-config
|
|
3
|
+
description: Set up the pi-bgrun digest scorecard for this project. Use when the user asks to configure a digest, enable digest heuristics, or when a pi-bgrun nudge points at this skill. Samples the project's real job logs, picks a shipped preset (go-test, jest, pytest, junit-xml) or drafts a custom digest command, validates it against green AND red logs, then writes the digest section into .pi/pi-bgrun.json.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Configure a project digest scorecard
|
|
7
|
+
|
|
8
|
+
Goal: a `digest` section in `<project>/.pi/pi-bgrun.json` whose command turns a
|
|
9
|
+
job log into a short pass/fail scorecard, appended to every `bgrun` wake as
|
|
10
|
+
`digest (<label>): ...`. The scorecard must be reliable on both green and red
|
|
11
|
+
logs — a wrong scorecard is worse than none. Most projects run more than one
|
|
12
|
+
kind of job (unit tests, a build, e2e); configure one entry per job type rather
|
|
13
|
+
than one command that guesses.
|
|
14
|
+
|
|
15
|
+
Config shapes — a single object (legacy) or an ordered **list** of entries; if
|
|
16
|
+
both `preset` and `command` are set within one entry, the preset wins:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{ "digest": { "preset": "go-test" } }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{ "digest": { "command": "grep -E 'FAIL|ok ' \"$1\" | head -5" } }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"digest": [
|
|
29
|
+
{ "type": "test", "preset": "go-test" },
|
|
30
|
+
{ "type": "build", "label": "build",
|
|
31
|
+
"command": "grep -E '^error' \"$1\" | head -5" },
|
|
32
|
+
{ "match": { "command": "*cargo*" }, "label": "cargo", "preset": "go-test" },
|
|
33
|
+
{ "preset": "go-test" }
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Prefer a `type` on each entry: it is matched exactly (case-insensitive) against
|
|
39
|
+
the `type:` the agent passes to `bgrun`, so it does not depend on job names or
|
|
40
|
+
command lines staying stable. When you configure a `type`, tell the agent to
|
|
41
|
+
pass it: `bgrun(command: …, name: …, type: "test")`. If a job's `type`/name
|
|
42
|
+
selects no entry, pi-bgrun logs a one-line diagnostic naming the job and the
|
|
43
|
+
configured types — check it when a scorecard is expected but absent.
|
|
44
|
+
|
|
45
|
+
Selection (exactly one entry, or none):
|
|
46
|
+
|
|
47
|
+
1. Entries with a `type` are checked **first**, in config order, and match only
|
|
48
|
+
a job declaring that exact type that also satisfies the entry's `match` if
|
|
49
|
+
it has one. First type match wins.
|
|
50
|
+
2. Otherwise the entries **without** a `type` are scanned in config order:
|
|
51
|
+
`match.name` / `match.command` globs and no-`match` defaults.
|
|
52
|
+
3. No match → no digest.
|
|
53
|
+
|
|
54
|
+
- `type` and `match` compose (AND): with both present, only a job of that type
|
|
55
|
+
that also satisfies the glob matches.
|
|
56
|
+
- `match.name` / `match.command` are **globs** tested against the job's
|
|
57
|
+
`name` and command line; both present → both must match. They are
|
|
58
|
+
**case-insensitive and whole-string** (`*` any run, `?` one character;
|
|
59
|
+
write `*text*` for a substring; `\` escapes the next character).
|
|
60
|
+
- An entry with no `match` (or an empty `match`) matches every job — put it
|
|
61
|
+
**last** as the default. Include one so jobs you did not anticipate still
|
|
62
|
+
get a scorecard.
|
|
63
|
+
- `label` sets the wake tag; without it a type entry uses its `type` string, a
|
|
64
|
+
glob entry uses the matched `match.name`, else the preset id (or `command`).
|
|
65
|
+
- An invalid `match` (non-string), an invalid `type`, or an entry with no valid
|
|
66
|
+
`preset`/`command` is dropped silently; an empty/all-invalid list counts as
|
|
67
|
+
unconfigured.
|
|
68
|
+
|
|
69
|
+
Shipped presets: `go-test` (package ok/FAIL counts + failing test names),
|
|
70
|
+
`jest` (Tests/Test Suites summary + failed test names), `pytest` (final
|
|
71
|
+
passed/failed/error summary line + FAILED test ids), `junit-xml`
|
|
72
|
+
(`<failure>`/`<error>` counts + failing testcase names). Each preset also
|
|
73
|
+
carries a `suggestedType` (all `test`) — use it as the `type` when scaffolding
|
|
74
|
+
an entry, e.g. `{ "type": "test", "preset": "go-test" }`. The suggestion is
|
|
75
|
+
advisory; a preset entry with no `type` still applies to every job.
|
|
76
|
+
|
|
77
|
+
## Procedure
|
|
78
|
+
|
|
79
|
+
1. **Find done-job logs.** Locate the project's jobsDir from the config
|
|
80
|
+
layering: in a project root it defaults to `<project>/.pi-bgrun/jobs`;
|
|
81
|
+
otherwise to the machine-global `~/.pi-bgrun/jobs`. An explicit `jobsDir`
|
|
82
|
+
(any config layer) or `PI_BGRUN_DIR` overrides it; `PI_BGRUN_GLOBAL_DIR`
|
|
83
|
+
retargets the machine-global base. The resolved path is printed as
|
|
84
|
+
`log: <path>` by `bgrun` and by `bgstatus <id>` — read it back there if
|
|
85
|
+
unsure. List the `*.log` files of finished jobs.
|
|
86
|
+
2. **Sample the formats across job types.** Group the logs by job type using
|
|
87
|
+
each job's `name` and command line (from `bgstatus`); most projects have at
|
|
88
|
+
least a test job and a build job. Pick 2-3 logs per type — at least one
|
|
89
|
+
green and one red run each — and inspect them with `ctx_execute_file`
|
|
90
|
+
(context-mode sandbox, so only your printed summary enters context).
|
|
91
|
+
Identify the runner / output format for each type, and name the type with a
|
|
92
|
+
short token (`test`, `build`, `lint`, `e2e`).
|
|
93
|
+
3. **Try a preset first, per type.** Run each shipped preset's command against
|
|
94
|
+
a sample log (`sh -c '<preset command>' sh <logpath>`). Preset commands are
|
|
95
|
+
data in the package's `extension/digestPresets.ts`. Clean scorecard on green
|
|
96
|
+
AND red samples → use that preset for that type, seeding the entry's
|
|
97
|
+
`type` from the preset's `suggestedType` (all shipped presets suggest
|
|
98
|
+
`test`). Repeat for each type.
|
|
99
|
+
4. **Draft custom commands** for types no preset fits. Use awk/sed/grep/jq; the
|
|
100
|
+
command receives the log path as `$1` and MUST end in `head -N` so output is
|
|
101
|
+
bounded. Keep it to a count line plus failed-item names.
|
|
102
|
+
5. **Validate each entry on green AND red.** Run every drafted command against
|
|
103
|
+
every sample for its type. Each must produce a correct scorecard on both: no
|
|
104
|
+
phantom failures on green logs, no missing failures on red ones. If a type
|
|
105
|
+
has no reliable command, omit that entry (or leave the digest unconfigured)
|
|
106
|
+
rather than shipping a wrong scorecard — say why.
|
|
107
|
+
6. **Write the config, one `type` entry per job type.** Merge the entries into
|
|
108
|
+
`<project>/.pi/pi-bgrun.json`, preserving any existing keys, giving each
|
|
109
|
+
entry the `type` you identified in step 2, and putting the no-`match`
|
|
110
|
+
default entry **last**. Use `match.name` / `match.command` globs only for
|
|
111
|
+
jobs that will not pass a `type`. Create the file if absent. Tell the user
|
|
112
|
+
(or the agent driving `bgrun`) which `type:` value to pass for each job.
|
|
113
|
+
7. **Smoke-test each entry.** Start a real `bgrun` job of each configured type
|
|
114
|
+
(e.g. the test command AND the build command), passing the matching
|
|
115
|
+
`type:`, and check that its wake carries a correct `digest (<label>):` block
|
|
116
|
+
for the right entry. If a block is empty, wrong, or comes from the wrong
|
|
117
|
+
entry, fix the command / type / ordering and repeat step 7.
|
package/skill/run-bg/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ no polling.
|
|
|
28
28
|
|
|
29
29
|
| Action | Tool |
|
|
30
30
|
|---|---|
|
|
31
|
-
| Start | `bgrun(command: "make test-short", name: "unit-tests")` → `started: <job-id>` (name is an optional short label; use it so jobs are recognizable in `bgstatus`, the status widget, and wake messages) |
|
|
31
|
+
| Start | `bgrun(command: "make test-short", name: "unit-tests", type: "test")` → `started: <job-id>` (name is an optional short label; use it so jobs are recognizable in `bgstatus`, the status widget, and wake messages) |
|
|
32
32
|
| Status | `bgstatus(<job-id>)` for one job, or `bgstatus()` for this session's running jobs — finished jobs are hidden by default; pass `includeDone: true` to list them |
|
|
33
33
|
| Tail | `bgtail(<job-id>, 40)` — first read: last-40 tail; later reads: only lines appended since (delta tailing) |
|
|
34
34
|
| Grep | `bggrep(<job-id>, "pattern", context?)` — line-numbered matches, capped and condensed; default pattern = generic failure signatures (override when you know the format) |
|
|
@@ -37,7 +37,9 @@ no polling.
|
|
|
37
37
|
## Workflow
|
|
38
38
|
|
|
39
39
|
1. **Start:** call `bgrun` with the command (and a short `name`, e.g. `name: "unit-tests"`).
|
|
40
|
-
|
|
40
|
+
When the project's digest config defines `type` entries, also pass the
|
|
41
|
+
matching `type` (e.g. `type: "test"`) — like `name`, it helps the wake
|
|
42
|
+
select the right digest scorecard. Note the returned job-id. Continue other
|
|
41
43
|
work; you will be woken automatically when the job finishes.
|
|
42
44
|
2. **On wake:** check the exit status in the wake message first.
|
|
43
45
|
- `exit: 0` → success. `bgtail` to confirm.
|
|
@@ -48,23 +50,37 @@ no polling.
|
|
|
48
50
|
- `done exit=<non-zero>` → failure; analyze the log.
|
|
49
51
|
- `running` but the job should have finished long ago → likely crashed (the
|
|
50
52
|
process died without writing the exit marker). Analyze the log with
|
|
51
|
-
`ctx_execute_file
|
|
53
|
+
`bggrep` (any jobs dir, last 2 MB) or `ctx_execute_file` on the absolute path
|
|
54
|
+
(whole file — needed for logs bigger than 2 MB).
|
|
52
55
|
|
|
53
56
|
### Reading results without flooding context
|
|
54
57
|
|
|
58
|
+
If the wake message carries a `digest (<label>):` block (the label is the
|
|
59
|
+
entry's `label`, its type, a matched `match.name`, or the preset id /
|
|
60
|
+
`command`), read that
|
|
61
|
+
first — it is a short pass/fail scorecard configured for this project and
|
|
62
|
+
usually answers "what failed" without any follow-up read. `bgtail` stays the
|
|
63
|
+
positional-peek tool for everything else.
|
|
64
|
+
|
|
55
65
|
- **Quick peek (≤40 lines):** call `bgtail` with the job id and `lines: 40` — strips the `__BGRUN_EXIT__` marker. The first read returns the last-40 tail; repeat reads return only lines appended since your last read (delta tailing) — polling a running job is nearly free.
|
|
56
|
-
- **Failure extraction:** `bggrep(<job-id>, "pattern")` — line-numbered matches with optional context lines, capped and condensed.
|
|
57
|
-
- **Whole-log failure analysis:** `ctx_execute_file` on the log
|
|
66
|
+
- **Failure extraction:** `bggrep(<job-id>, "pattern")` — line-numbered matches with optional context lines, capped and condensed. Resolves the job id to the configured jobs dir itself — no path to reconstruct. (`ctx_execute_file` can read the same file given its absolute path.) Searches the last 2 MiB by default; `bytes: 67108864` widens it to the whole capped log — more scanning costs latency and memory, **not context**, since the returned matches stay capped. Pass your own pattern whenever you know the tool's output format; the default only catches common failure signatures.
|
|
67
|
+
- **Whole-log failure analysis:** `ctx_execute_file` on the log's **absolute
|
|
68
|
+
path**. Unlike `bgtail`/`bggrep` (bounded to the last 2 MB), this reads the
|
|
69
|
+
whole file — the only way to cover a log bigger than 2 MB, e.g. one that hit
|
|
70
|
+
the size ceiling. Copy the `log:` path from `bgrun`'s `started:` line and
|
|
71
|
+
expand `~` yourself (it is not expanded for you; the tool takes an absolute
|
|
72
|
+
path or one relative to the project root). Otherwise it is an ordinary tool
|
|
73
|
+
call: your normal Read-deny rules still apply.
|
|
58
74
|
|
|
59
75
|
```javascript
|
|
60
76
|
ctx_execute_file(
|
|
61
|
-
path: "
|
|
77
|
+
path: "/Users/me/project/.pi-bgrun/jobs/<JOB>.log",
|
|
62
78
|
language: "javascript",
|
|
63
79
|
code: "const L=FILE_CONTENT.split('\\n'); \
|
|
64
80
|
const fails=L.filter(l=>/(--- FAIL|FAIL|panic:|Error:)/.test(l)); \
|
|
65
81
|
console.log(`lines: ${L.length}, failures: ${fails.length}`); \
|
|
66
82
|
console.log(fails.slice(0,40).join('\\n'));"
|
|
67
|
-
)
|
|
83
|
+
})
|
|
68
84
|
```
|
|
69
85
|
|
|
70
86
|
A 10 000-line `make test` log collapses to a ~30-line summary in context.
|
|
@@ -74,17 +90,36 @@ no polling.
|
|
|
74
90
|
- `bash grep` output is uncapped — a retry-storm log can dump thousands of
|
|
75
91
|
matching lines (megabytes) straight into context, and staying safe depends
|
|
76
92
|
on remembering `| head` on every single call. `bggrep` is bounded by design
|
|
77
|
-
(
|
|
93
|
+
(last 2 MB of the log, per-line 10 000-char pre-truncation, ~50 matches,
|
|
94
|
+
~8KB, plus a wall-clock match budget so a runaway regex errors instead of
|
|
95
|
+
hanging).
|
|
78
96
|
- It takes the job id — no log-path reconstruction, no shell-quoting of the
|
|
79
|
-
regex
|
|
80
|
-
project-sandboxed `ctx_execute_file` cannot reach.
|
|
97
|
+
regex, and no reliance on the agent getting `~` expansion right.
|
|
81
98
|
- Output is self-describing: match count, line numbers, `…[N skipped]…` gap
|
|
82
99
|
markers, `— none` for no-match.
|
|
83
100
|
|
|
84
101
|
Plain `grep` via bash is fine only for a one-off search you know is tiny.
|
|
85
102
|
|
|
86
103
|
**Never `cat`, `Read`, `bash cat`, or `bash grep` a full bgrun log.** Always
|
|
87
|
-
`bgtail`, `bggrep`, or `ctx_execute_file`.
|
|
104
|
+
`bgtail`, `bggrep`, or (for project-local logs) `ctx_execute_file`.
|
|
105
|
+
|
|
106
|
+
**Order of preference, cheapest first: `bgtail` → `bggrep` → `ctx_execute_file`.**
|
|
107
|
+
Reach for the sandbox only when you need something a regex over lines cannot
|
|
108
|
+
express — totals, dedup, grouping, joining the log against another file.
|
|
109
|
+
|
|
110
|
+
`ctx_execute_file` is not itself a context dump: the file's bytes never enter
|
|
111
|
+
context, only your script's **stdout** does ("raw content never leaves"). So the
|
|
112
|
+
cost is exactly what you print — which makes `console.log(FILE_CONTENT)` (or
|
|
113
|
+
`print(open(path).read())`, or a big unbounded slice) the one way a whole-log
|
|
114
|
+
analysis turns into a context dump, and a capped-by-default 64 MiB log makes
|
|
115
|
+
that expensive rather than merely rude. Aggregate, then cap what you print:
|
|
116
|
+
|
|
117
|
+
- print counts / grouped summaries / the first N matches — not the content;
|
|
118
|
+
- keep a `.slice(0, 40)` / `[:40]` on anything you echo;
|
|
119
|
+
- for many different questions about one big log, index it once (`ctx_index`)
|
|
120
|
+
and `ctx_search` it, instead of re-scanning the file per call;
|
|
121
|
+
- `bgtail` with a larger `lines`, or a tighter `bggrep` pattern, is usually the
|
|
122
|
+
cheaper answer to "I need to see more".
|
|
88
123
|
|
|
89
124
|
## After a pi restart or session switch
|
|
90
125
|
|
|
@@ -93,23 +128,45 @@ Plain `grep` via bash is fine only for a one-off search you know is tiny.
|
|
|
93
128
|
- After a restart/switch, run `bgstatus(<job-id>)` — the id still resolves via the
|
|
94
129
|
log's `__BGRUN_EXIT__=N` marker. To browse everything on disk, use
|
|
95
130
|
`bgstatus(includeDone: true)`.
|
|
96
|
-
- Each session only tracks its own jobs by default.
|
|
97
|
-
appear only when `adoptForeignJobs` is enabled in
|
|
98
|
-
(or `PI_BGRUN_FOREIGN_JOBS=1`)
|
|
131
|
+
- Each session only tracks its own jobs by default. Running jobs from other
|
|
132
|
+
sessions appear only when `adoptForeignJobs` is enabled in
|
|
133
|
+
`~/.pi/agent/pi-bgrun.json` (or `PI_BGRUN_FOREIGN_JOBS=1`); finished foreign
|
|
134
|
+
logs appear with `bgstatus(includeDone: true)` regardless.
|
|
99
135
|
|
|
100
136
|
## Rules
|
|
101
137
|
|
|
102
138
|
- Call the tools; never hand-roll `nohup … &` inline.
|
|
103
139
|
- One job = one id. Multiple concurrent jobs are fine — each has its own log.
|
|
104
|
-
-
|
|
105
|
-
`
|
|
106
|
-
|
|
107
|
-
|
|
140
|
+
- Job logs are capped by default (`maxLogBytes` / `PI_BGRUN_MAX_LOG_BYTES`,
|
|
141
|
+
64 MiB; `0` = unlimited) and the cap keeps the **first** bytes. A log that
|
|
142
|
+
ends with `__BGRUN_TRUNC__ output truncated: kept the first <N> bytes` (or
|
|
143
|
+
`__BGRUN_NOCAP__ log ceiling unavailable`, when the ceiling could not be
|
|
144
|
+
installed and the job ran uncapped) hit that ceiling: output past it was dropped, not lost to a failure — the job
|
|
145
|
+
still ran to completion with its real exit code, and readers (`bgtail`,
|
|
146
|
+
`bggrep`, the wake's line count/last line) filter the notice out. The wake's
|
|
147
|
+
Stats line, `bgtail` and `bggrep` all say when a log was capped (and report
|
|
148
|
+
`truncatedAtBytes` in their details), and a configured digest scorecard is
|
|
149
|
+
skipped rather than scored against an incomplete log — so on a capped job,
|
|
150
|
+
read a missing digest as "unknown", **not** as "no failures", and do not
|
|
151
|
+
re-run the command to see the missing tail; raise the ceiling if you need the
|
|
152
|
+
whole log. The flag lives in the exit marker (`__BGRUN_EXIT__=0
|
|
153
|
+
truncated=<N>`, or `nocap=1`), never in printable text — the `__BGRUN_*__`
|
|
154
|
+
lines are reserved, so a notice-looking line printed by the command itself is
|
|
155
|
+
content, not a signal. A search window is also limited to its last
|
|
156
|
+
500 000 lines: when that bites, `bgtail`/`bggrep` say so — a "none" from a
|
|
157
|
+
trimmed window means the head was not searched.
|
|
158
|
+
- Logs default to `<project>/.pi-bgrun/jobs` in a repo — project-scoped is the
|
|
159
|
+
model (`~/.pi-bgrun/jobs` is a deprecated fallback for a cwd with no project
|
|
160
|
+
root; an absolute `PI_BGRUN_DIR`/`jobsDir` still works but is legacy). Project-local dirs are
|
|
161
|
+
auto-ignored via `.git/info/exclude`, which keeps `git status` clean; the
|
|
162
|
+
logs stay reachable for project-sandboxed analysis tools like
|
|
163
|
+
`ctx_execute_file` because they live inside the project.
|
|
108
164
|
- Cleanup: `bgclean` removes only THIS session's old logs; `bgclean all`
|
|
109
165
|
sweeps every session's. Auto-sweeps at session start/shutdown are
|
|
110
|
-
session-scoped plus
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
166
|
+
session-scoped plus an orphan pass (default on — removes finished week-old
|
|
167
|
+
logs from crashed/abandoned sessions; disable with `globalAutoClean: false`
|
|
168
|
+
/ `PI_BGRUN_GLOBAL_AUTO_CLEAN=0`). Under the project-local default the orphan
|
|
169
|
+
pass covers the current project's jobs dir AND the machine-global
|
|
170
|
+
`~/.pi-bgrun/jobs`; an explicit absolute `jobsDir` is swept alone. Retention
|
|
171
|
+
is `cleanupDays` (default 7, configurable).
|
|
172
|
+
- To stop a running job, use `bash` with `kill -- -<pid>` (process group — required because the child is spawned detached). The pid is the last `--`-separated segment of the job id; it is not shown as a separate field in `bgstatus` output. There is no `bgkill` tool.
|