Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ jobs:
node-version: '24'
package-manager-cache: false
# Dependency-free; native Herdr checks run locally, not on headless CI.
- run: npm test --prefix herdr-insights
- run: npm run check --prefix herdr-insights
- run: npm run test:insights
- run: npm run check:insights

secrets:
uses: ./.github/workflows/secret-scan.yml
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,19 @@ pi install npm:@gagansd/pi-automode # Permission gate (opt-in, off by default)

| Mod | Description | Install |
| :--- | :--- | :--- |
| [Insights](./herdr-insights/) | Local-first token-usage dashboard with model rankings, usage trends, and an activity heatmap. Reads Pi, Codex, Claude Code, and OpenCode sessions. | From a checkout: `npm run install:local --prefix herdr-insights -- --open` |
| [Insights](./herdr-insights/) | Local-first token-usage dashboard with model rankings, usage trends, and an activity heatmap. Reads Pi, Codex, Claude Code, and OpenCode sessions. | From a checkout: `npm run install:local --prefix herdr-insights` |

### Install Insights

Requires [Herdr](https://herdr.dev) and **Node.js 22.15+**. No dependencies or account needed. From this repository's root:

```bash
npm run install:local --prefix herdr-insights -- --open
npm run install:local --prefix herdr-insights
```

The installer validates the mod with `herdr-app check` and opens an **Insights** tab. It installs only `mods/insights/` under `~/.config/herdr-app`, leaving layouts, keys, and terminals unchanged. No Pi extension installation is required.
The installer validates the mod with `herdr-app check` without opening a tab or test window. Add `-- --open` to open one **Insights** tab. It installs only `mods/insights/` under `~/.config/herdr-app`, leaving layouts, keys, and terminals unchanged. No Pi extension installation is required.

See the [Insights README](./herdr-insights/README.md) for standalone checkout instructions, settings, privacy details, and uninstallation, or [RECIPE.md](./herdr-insights/RECIPE.md) for the mod specification.
See the [Insights README](./herdr-insights/README.md) for standalone checkout instructions, settings, privacy details, and uninstallation, [INSTALL.md](./herdr-insights/INSTALL.md) for agent installation instructions, or [RECIPE.md](./herdr-insights/RECIPE.md) for the rebuild specification.

---

Expand All @@ -124,10 +124,14 @@ After `pi install .`, local edits apply on `/reload`.
Insights uses Node.js `22.15+`; its native validation requires Herdr. Run its checks separately from the root Pi extension suite:

```bash
npm test --prefix herdr-insights
npm run check --prefix herdr-insights
npm run test:insights
npm run check:insights
```

These are developer checks, not installation steps. See the
[Insights development guide](./docs/herdr-insights-development.md) for opt-in
native release validation.

### Shared token counter

After installing root development dependencies, count literal text locally (no API calls):
Expand Down
72 changes: 72 additions & 0 deletions docs/herdr-insights-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Insights development and release validation

**Not an installation guide.** To install the existing build, use
[`herdr-insights/INSTALL.md`](../herdr-insights/INSTALL.md). The self-contained
[`RECIPE.md`](../herdr-insights/RECIPE.md) remains the Herdr rebuild brief.

## Repository boundaries

- `herdr-insights/`: shareable distribution; docs, preview, mod source, and
dependency-free install/uninstall scripts. This folder installs by itself.
- `tests/herdr-insights/`: unit/integration tests, fixtures, and native smoke tests.
- `scripts/herdr-insights/`: developer package/distribution checks.

Keeping developer tooling outside the shared folder reduces the files an
installing agent needs to review. Herdr's own `AGENTS.md` still controls security
review; this separation does not exempt the mod source from that review. Moving
files out of the distribution does not remove any test coverage.

## Headless checks

From the repository root, with Node.js 22.15+ (CI uses Node 24):

```sh
npm run test:insights
npm run check:insights
```

No dependency installation is needed. CI runs both commands, including accounting,
deduplication, configuration, incremental recovery, safe install/uninstall,
distribution structure, PNG, recipe, and installation-boundary regressions.
Neither command launches a Herdr window. Installer tests use a fake Herdr runner.

## Native release checks — opt-in, not installation steps

Requires Herdr on a graphical machine. The populated-render harness runs
`herdr-app check` against a temporary mod copy without collecting user transcripts:

```sh
node tests/herdr-insights/native-loaded-check.mjs --rows 10000
node tests/herdr-insights/native-loaded-check.mjs --rows 10000 --mode menu
node tests/herdr-insights/native-loaded-check.mjs --rows 10000 --mode settings
```

**The async harness opens a separate, visible Herdr window.** Tell the operator
before running it. It refuses to run without `--allow-window`; it is deliberately
not invoked by installation, headless tests, or package checks:

```sh
node tests/herdr-insights/native-async-check.mjs --allow-window
node tests/herdr-insights/native-async-check.mjs --allow-window --settings --appearance light
node tests/herdr-insights/native-async-check.mjs --allow-window --menu
```

It uses a temporary app home with fixture data, captures a screenshot and status,
and stops only the process it started. Synthetic rows must never be installed
into a user's app home. Inspect screenshots and logs for rendering errors. Verify
filters, heatmap modes, settings, refresh, and empty/error states before release.

Reinstall validation must preserve source transcripts, layouts, keys, local edits,
and `insights.json`; the automated installer tests cover those boundaries.

## Sharing a build

Keep `herdr-insights/` self-contained, with `RECIPE.md`, `INSTALL.md`, `README.md`,
`preview.png`, and `mods/insights/`. Do not ship layouts, key snippets, developer
tests/fixtures, or smoke-test tooling in that folder. The installer copies only
the mod tree, generates the local Node path, and records ownership separately.

For Herdr Community discovery, publish the folder in a public GitHub repo and
add topic `herdr-mod` after merging to the default branch. Allow up to an hour for
indexing and at least 24 hours for picture screening. Do not republish or change
repository visibility as part of an end-user installation.
36 changes: 36 additions & 0 deletions herdr-insights/INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Install Insights

Requires Herdr and **Node.js 22.15+**. Reuse an existing checkout when available.
Follow Herdr's `AGENTS.md`; this guide does not override its review requirements.
`RECIPE.md` describes rebuilding, not installation.

## Install

From the parent of `herdr-insights/`:

```sh
npm run install:local --prefix herdr-insights
```

Only that folder is needed; no `npm install` is required. The installer copies
the mod, records ownership, configures the local Node path, and runs
`herdr-app check`. Existing usage data, layouts, keys, and terminals stay intact.

Finish when the installer exits successfully with **`herdr-app check passed`**.
Report failures and stop; do not force overwrites or rebuild to bypass errors.
Do not run developer tests or change tabs/focus during installation. Never
launch a second Herdr instance.

## Open only if requested

```sh
herdr-app do tab.new '{"name":"Insights","mod":"insights"}'
```

## Uninstall

Usage data and locally edited/unowned files are preserved:

```sh
npm run uninstall:local --prefix herdr-insights
```
32 changes: 15 additions & 17 deletions herdr-insights/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,24 @@ Reads local sessions from **Pi, Codex, Claude Code, and OpenCode**.

## Install

Installing requires Herdr and **Node.js 22.15+**. No dependencies or account needed.
The uninstaller also runs on older Node versions.

Ask your agent:

> Read https://github.com/GaganSD/coding-agent-toolkit/tree/main/herdr-insights and install the Insights mod following its RECIPE.md and my Herdr AGENTS.md.
```
Read https://github.com/GaganSD/coding-agent-toolkit/blob/main/herdr-insights/INSTALL.md and install Insights following it.
```

Or install from a checkout:

```sh
git clone https://github.com/GaganSD/coding-agent-toolkit.git
cd coding-agent-toolkit
npm run install:local --prefix herdr-insights -- --open
npm run install:local --prefix herdr-insights
```

The installer runs `herdr-app check`; with `--open` it then opens an **Insights**
tab. It installs owned files only under `mods/insights/` and writes its install
manifest to `data/insights/` in `~/.config/herdr-app`; your layouts, keys, and
terminals are unchanged. Set `HERDR_APP_HOME` to use a different app home.
The installer runs `herdr-app check` without opening a tab or test window.
Follow [INSTALL.md](INSTALL.md) for the installation steps. Add `-- --open` only
if you want it to open one **Insights** tab. It installs owned files only under
`mods/insights/` and writes its install manifest to `data/insights/` in
`~/.config/herdr-app`; your layouts, keys, and terminals are unchanged. Set `HERDR_APP_HOME` to use a different app home.

## Use

Expand All @@ -46,14 +45,13 @@ response text or modify source transcripts.

## Development

[RECIPE.md](RECIPE.md) describes the mod's behavior, architecture, and rebuild
requirements. The distribution folder is `herdr-insights`; its native mod ID is
`insights` and its display name is **Insights**. No layouts or key bindings ship.
[RECIPE.md](RECIPE.md) is the self-contained rebuild brief required by Herdr.
The distribution folder is `herdr-insights`; its native mod ID is `insights`
and its display name is **Insights**. No layouts or key bindings ship.

```sh
npm test --prefix herdr-insights
npm run check --prefix herdr-insights
```
Developer tests, fixtures, and release validation live outside this distribution;
see the repo's [development guide](../docs/herdr-insights-development.md). They are
not installation steps.

Uninstall while keeping usage data:

Expand Down
19 changes: 13 additions & 6 deletions herdr-insights/RECIPE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and opens in its own tab without changing terminals, layouts, or keys.

![Insights](preview.png)

Installing this existing build? Follow [INSTALL.md](INSTALL.md) and the user's
Herdr `AGENTS.md`. This recipe is a rebuild brief; its **Done when** checks apply
to building/releasing, not routine installation. Installation does not launch
smoke-test windows or open a tab unless requested.

**What you see:** A vertically scrollable dashboard centered at a maximum width
of 1180px. Use Herdr theme colors, 1px borders, 8px card corners, 6px control
corners, no decorative shadows, and 4/8/12/16/24px spacing. Page padding is 12px
Expand Down Expand Up @@ -53,9 +58,10 @@ controls preserve custom mappings. Changing source configuration invalidates the
scan cache; palette/cache/week-start/refresh changes preserve collected history.

**Built from:** Put this distribution in `herdr-insights/`, with `RECIPE.md`,
`README.md`, `preview.png`, and `mods/insights/`. Do not include a `layouts/` folder
or keys snippet: no bindings are needed. The recipe heading and `mod.json` title
are **Insights**; the native mod ID is `insights`.
`README.md`, `INSTALL.md`, `preview.png`, and `mods/insights/`. Keep developer
tests, fixtures, and release-check tooling outside this distribution folder.
Do not include a `layouts/` folder or keys snippet: no bindings are needed.
The recipe heading and `mod.json` title are **Insights**; the native mod ID is `insights`.

`mods/insights/mod.json` contains `{"place":"none","title":"Insights"}`.
`main.js` default-exports a gpui-kit `View`; use gpui-base flex/controls, `theme()`
Expand Down Expand Up @@ -115,9 +121,10 @@ a backup before rebuilding; read-only prepare must not repair or create stores.
Never write layouts, keys, source transcripts, or an existing user's usage data
from the installer. Do not copy an absolute runtime path from another machine.

**Done when:** Package tests and checks pass, including distribution naming,
PNG preview, recipe structure, no layouts, token accounting, deduplication,
configuration validation, incremental recovery, and safe installation/uninstallation.
**Done when:** For a new build or release (not an installation), package tests
and checks pass, including distribution naming, PNG preview, recipe structure,
no layouts, token accounting, deduplication, configuration validation, incremental
recovery, and safe installation/uninstallation.
Run `herdr-app check` on the installed mod, plus populated-render and asynchronous
native smoke checks in temporary app homes. Inspect a native screenshot and status
for render errors. Verify actual filters, heatmap modes, source settings, refresh,
Expand Down
4 changes: 1 addition & 3 deletions herdr-insights/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
"node": ">=22.15"
},
"scripts": {
"test": "node --test tests/*.test.mjs",
"install:local": "node scripts/install.mjs",
"uninstall:local": "node scripts/uninstall.mjs",
"check": "node scripts/check.mjs"
"uninstall:local": "node scripts/uninstall.mjs"
}
}
6 changes: 4 additions & 2 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ Packages and mods live at the repository root, not under `packages/`. Pi extensi

## Herdr mods

- [Insights installation](herdr-insights/INSTALL.md): agent installation steps and completion criteria
- [Insights README](herdr-insights/README.md): installation, settings, privacy, development, uninstallation
- [Insights recipe](herdr-insights/RECIPE.md): native mod specification, architecture, rebuild requirements
- [Insights manifest](herdr-insights/package.json): local install, uninstall, test, and check scripts; not published on npm
- [Insights manifest](herdr-insights/package.json): local install and uninstall scripts; not published on npm
- [Insights development](docs/herdr-insights-development.md): tests and opt-in native release validation

Insights requires Herdr and Node.js 22.15+. From a checkout, run `npm run install:local --prefix herdr-insights -- --open`. The installer validates and opens the mod without changing layouts, keys, or terminals. Session data stays local; no account or API key is required. Run Insights tests separately with `npm test --prefix herdr-insights` and native checks with `npm run check --prefix herdr-insights`.
For installation, follow herdr-insights/INSTALL.md and the user's Herdr AGENTS.md. Insights requires Herdr and Node.js 22.15+. From a checkout, run `npm run install:local --prefix herdr-insights`; it runs `herdr-app check` without opening tabs or test windows. Add `-- --open` only when requested. Layouts, keys, and terminals are unchanged. Session data stays local; no account or API key is required. RECIPE.md is the rebuild brief, not an installation test plan. Developer checks (`npm run test:insights`, `npm run check:insights`) and opt-in native smoke tests live outside the distribution; see docs/herdr-insights-development.md.

## Agent indexes

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"typecheck": "npm run typecheck --prefix pi-slate && npm run typecheck --prefix pi-ask && npm run typecheck --prefix pi-web-search && npm run typecheck --prefix pi-subagents && npm run typecheck --prefix pi-automode",
"lint": "eslint . --max-warnings=0",
"test:tooling": "node --test scripts/tests/*.test.mjs",
"test:insights": "node --test tests/herdr-insights/*.test.mjs",
"check:insights": "node scripts/herdr-insights/check.mjs",
"secrets:scan": "bash scripts/secret-scan.sh",
"hooks:install": "bash scripts/install-hooks.sh",
"release:prepare": "node scripts/release.mjs prepare",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
assertSupportedNodeVersion,
collectSourceFiles,
lstatOrNull,
} from "./lib.mjs";
} from "../../herdr-insights/scripts/lib.mjs";
import { inspectDistribution } from "./distribution.mjs";

const TEST_ROOT = path.resolve(PACKAGE_ROOT, "../tests/herdr-insights");
const errors = [];
const notes = [];

Expand Down Expand Up @@ -102,9 +103,7 @@ async function checkPackage() {
}

const expectedScripts = {
check: "node scripts/check.mjs",
"install:local": "node scripts/install.mjs",
test: "node --test tests/*.test.mjs",
"uninstall:local": "node scripts/uninstall.mjs",
};
for (const [name, expected] of Object.entries(expectedScripts)) {
Expand All @@ -117,15 +116,14 @@ async function checkPackage() {
async function checkFiles() {
for (const relative of [
".gitignore",
"INSTALL.md",
"RECIPE.md",
"preview.png",
"mods/insights/main.js",
"mods/insights/mod.json",
"scripts/install.mjs",
"scripts/lib.mjs",
"scripts/uninstall.mjs",
"tests/install.test.mjs",
"tests/node-version.test.mjs",
]) {
const stat = await lstatOrNull(path.join(PACKAGE_ROOT, relative));
if (!stat || !stat.isFile()) fail(`Missing owned file: ${relative}`);
Expand Down Expand Up @@ -165,8 +163,8 @@ async function checkDocs() {
}

async function checkScriptsAndTests() {
const directories = ["scripts", "tests"]
.map((relative) => path.join(PACKAGE_ROOT, relative));
const directories = [path.join(PACKAGE_ROOT, "scripts"),
path.resolve(PACKAGE_ROOT, "../scripts/herdr-insights"), TEST_ROOT];
for (const directory of directories) {
const files = (await walkFiles(directory)).filter((file) =>
/\.(?:mjs|js)$/.test(file),
Expand All @@ -190,10 +188,10 @@ async function checkScriptsAndTests() {
}

async function checkTestInventory() {
const tests = (await walkFiles(path.join(PACKAGE_ROOT, "tests"))).filter((file) =>
const tests = (await walkFiles(TEST_ROOT)).filter((file) =>
/install.*\.test\.mjs$/.test(path.basename(file)),
);
if (tests.length === 0) fail("No tests/install*.test.mjs files found");
if (tests.length === 0) fail("No tests/herdr-insights/install*.test.mjs files found");
}

async function checkDistribution() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { promises as fs } from "node:fs";
import path from "node:path";

import { lstatOrNull } from "./lib.mjs";
import { lstatOrNull } from "../../herdr-insights/scripts/lib.mjs";
import { validatePng } from "./png.mjs";

export async function inspectDistribution(packageRoot) {
Expand All @@ -14,6 +14,7 @@ export async function inspectDistribution(packageRoot) {
for (const [relative, message] of [
["layouts", "Herdr mods must not ship layouts/"],
["keys.json", "Herdr mods must not ship keys.json"],
["tests", "Developer tests and fixtures must stay outside herdr-insights/"],
]) {
try {
if (await lstatOrNull(path.join(packageRoot, relative))) errors.push(message);
Expand All @@ -22,6 +23,15 @@ export async function inspectDistribution(packageRoot) {
}
}

try {
const allowed = new Set(["install.mjs", "lib.mjs", "uninstall.mjs"]);
for (const name of await fs.readdir(path.join(packageRoot, "scripts"))) {
if (!allowed.has(name)) errors.push(`Developer tooling must stay outside herdr-insights/scripts/: ${name}`);
}
} catch (error) {
if (error.code !== "ENOENT") errors.push(`Cannot inspect install scripts: ${error.message}`);
}

try {
const preview = await fs.readFile(path.join(packageRoot, "preview.png"));
const image = validatePng(preview);
Expand Down
File renamed without changes.
Loading
Loading