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
5 changes: 5 additions & 0 deletions .changeset/add-describe-verb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ghost-drift": minor
---

Add `ghost-drift describe` — prints a section map of `fingerprint.md` (frontmatter range, body sections, per-dimension decision blocks) with line ranges and token estimates, so host agents can selectively load only the sections they need instead of the whole file. The review and generate skill recipes now open with `describe` and teach a "load whole `# Decisions` block if uncertain" recall safety rule.
2 changes: 1 addition & 1 deletion apps/docs/src/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const sections: {
name: "CLI Reference",
href: "/tools/drift/cli",
description:
"Six deterministic primitives — compare, lint, ack, adopt, diverge, emit. Plus the skill recipes the host agent runs.",
"Seven deterministic primitives — compare, lint, describe, ack, adopt, diverge, emit. Plus the skill recipes the host agent runs.",
icon: <BookOpen className="size-8" strokeWidth={1.5} />,
},
];
Expand Down
52 changes: 51 additions & 1 deletion apps/docs/src/content/docs/cli-reference.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: CLI Reference
description: Six deterministic primitives. Everything interpretive lives in the skill bundle.
description: Seven deterministic primitives. Everything interpretive lives in the skill bundle.
kicker: Docs
section: drift
order: 20
Expand Down Expand Up @@ -69,6 +69,56 @@ ghost-drift lint path/to/fingerprint.md --format json

</DocSection>

<DocSection title="Inspection — describe">

Print a section map of `fingerprint.md` — frontmatter range, body sections
(`# Character`, `# Signature`, `# Decisions`, `# Fragments`), and each
`### dimension` block under Decisions, with line ranges and token estimates.
The host agent uses this to load only the sections it needs instead of the
whole file.

A typical `fingerprint.md` runs 3–5k tokens. The `# Decisions` block alone
is usually 60–80% of that, and an agent reviewing a single component change
rarely needs every dimension. `describe` is the deterministic answer to
"what's in this file and where" — the recall safety rule (when in doubt,
load the whole `# Decisions` block) lives in the review/generate skill
recipes.

<CliHelp command="describe" hideDescription />

```bash
# Default — reads ./fingerprint.md
ghost-drift describe

# Specific file
ghost-drift describe path/to/fingerprint.md

# Machine-readable for agents
ghost-drift describe --format json
```

Sample output (against `packages/ghost-ui/fingerprint.md`):

```text
fingerprint.md — 309 lines, ~3,955 tokens

FRONTMATTER 1–164 ~973 tok [palette, spacing, typography, surfaces, roles, observation, decisions]
# Character 166–169 ~159 tok
# Signature 170–179 ~298 tok
# Decisions 180–305 ~2,503 tok
### color-strategy 182–192 ~250 tok
### shape-language 205–216 ~181 tok
### typography-voice 217–229 ~258 tok
# Fragments 306–309 ~21 tok
```

Line ranges are 1-indexed and inclusive — they plug directly into a Read
tool's `offset` / `limit = end - start + 1`. Token counts are a `chars / 4`
approximation, sufficient for context budgeting.

</DocSection>

<DocSection title="Intent — ack / adopt / diverge">

These three verbs write per-dimension stances to `.ghost-sync.json`. `ack`
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ slug: getting-started
<DocSection title="The Shape of Ghost">

Ghost is split across two surfaces. The **CLI** is a set of deterministic
primitives — six verbs that never call an LLM. The **skill bundle** is a set
primitives — seven verbs that never call an LLM. The **skill bundle** is a set
of [agentskills.io](https://agentskills.io)-compatible recipes your host
agent (Claude Code, Cursor, Goose, Codex, …) follows for anything
interpretive: profile, review, verify, generate, discover.
Expand Down
17 changes: 16 additions & 1 deletion apps/docs/src/generated/cli-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-04-22T13:46:27.308Z",
"generatedAt": "2026-04-23T10:40:33.455Z",
"commands": [
{
"name": "compare",
Expand Down Expand Up @@ -55,6 +55,21 @@
}
]
},
{
"name": "describe",
"rawName": "describe [fingerprint]",
"description": "Print a section map of fingerprint.md (line ranges + token estimates) so agents can selectively load only the sections they need.",
"options": [
{
"rawName": "--format <fmt>",
"name": "format",
"description": "Output format: cli or json",
"default": "cli",
"takesValue": true,
"negated": false
}
]
},
{
"name": "ack",
"rawName": "ack",
Expand Down
30 changes: 30 additions & 0 deletions packages/ghost-drift/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
formatComparisonJSON,
formatCompositeComparison,
formatCompositeComparisonJSON,
formatLayout,
formatSemanticDiff,
formatTemporalComparison,
formatTemporalComparisonJSON,
layoutFingerprint,
lintFingerprint,
loadFingerprint,
readHistory,
Expand Down Expand Up @@ -152,6 +154,34 @@ export function buildCli(): ReturnType<typeof cac> {
}
});

// --- describe ---
cli
.command(
"describe [fingerprint]",
"Print a section map of fingerprint.md (line ranges + token estimates) so agents can selectively load only the sections they need.",
)
.option("--format <fmt>", "Output format: cli or json", { default: "cli" })
.action(async (path: string | undefined, opts) => {
try {
const target = resolve(process.cwd(), path ?? FINGERPRINT_FILENAME);
const raw = await readFile(target, "utf-8");
const layout = layoutFingerprint(raw);
if (opts.format === "json") {
process.stdout.write(
`${JSON.stringify({ path: target, ...layout }, null, 2)}\n`,
);
} else {
process.stdout.write(`${formatLayout(layout, target)}\n`);
}
process.exit(0);
} catch (err) {
console.error(
`Error: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(2);
}
});

registerAckCommand(cli);
registerAdoptCommand(cli);
registerDivergeCommand(cli);
Expand Down
5 changes: 5 additions & 0 deletions packages/ghost-drift/src/core/fingerprint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export {
serializeEmbeddingFragment,
} from "./fragments.js";
export type { FingerprintMeta, FrontmatterData } from "./frontmatter.js";
export type {
FingerprintLayout,
FingerprintLayoutSection,
} from "./layout.js";
export { formatLayout, layoutFingerprint } from "./layout.js";
export type {
LintIssue,
LintOptions,
Expand Down
Loading
Loading