ctxr-cli 0.2.2 → 0.3.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/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/builtin/index.d.ts +1 -4
- package/dist/adapters/builtin/index.d.ts.map +1 -1
- package/dist/adapters/builtin/index.js +1 -4
- package/dist/adapters/builtin/index.js.map +1 -1
- package/dist/adapters/types.d.ts +3 -15
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js +0 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/commands/adapters-generate.d.ts +3 -5
- package/dist/commands/adapters-generate.d.ts.map +1 -1
- package/dist/commands/adapters-generate.js +3 -15
- package/dist/commands/adapters-generate.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/session-capture.d.ts +0 -1
- package/dist/commands/session-capture.d.ts.map +1 -1
- package/dist/commands/session-capture.js +33 -41
- package/dist/commands/session-capture.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -4
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +2 -4
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.d.ts +0 -11
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -22
- package/dist/config/schema.js.map +1 -1
- package/dist/core/agents-doc.d.ts +0 -12
- package/dist/core/agents-doc.d.ts.map +1 -1
- package/dist/core/agents-doc.js +0 -26
- package/dist/core/agents-doc.js.map +1 -1
- package/dist/core/checks/integrity-checks.d.ts +2 -0
- package/dist/core/checks/integrity-checks.d.ts.map +1 -1
- package/dist/core/checks/integrity-checks.js +31 -1
- package/dist/core/checks/integrity-checks.js.map +1 -1
- package/dist/core/checks/manifest.d.ts.map +1 -1
- package/dist/core/checks/manifest.js +0 -2
- package/dist/core/checks/manifest.js.map +1 -1
- package/dist/core/errors.d.ts +13 -9
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +14 -27
- package/dist/core/errors.js.map +1 -1
- package/dist/core/fs/fenced-region.d.ts +18 -0
- package/dist/core/fs/fenced-region.d.ts.map +1 -1
- package/dist/core/fs/fenced-region.js +60 -0
- package/dist/core/fs/fenced-region.js.map +1 -1
- package/dist/core/procedures.d.ts.map +1 -1
- package/dist/core/procedures.js +38 -460
- package/dist/core/procedures.js.map +1 -1
- package/dist/core/reconcile.d.ts +3 -4
- package/dist/core/reconcile.d.ts.map +1 -1
- package/dist/core/reconcile.js +20 -12
- package/dist/core/reconcile.js.map +1 -1
- package/dist/core/write-lifecycle/path-gate.d.ts.map +1 -1
- package/dist/core/write-lifecycle/path-gate.js +13 -9
- package/dist/core/write-lifecycle/path-gate.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +4 -43
- package/dist/run.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/templates/skills/ctxr-connection-finding.md +15 -0
- package/templates/skills/ctxr-connection-proposal.md +17 -0
- package/templates/skills/ctxr-derived-artifacts.md +25 -0
- package/templates/skills/ctxr-ingest-orchestration.md +42 -0
- package/templates/skills/ctxr-land.md +19 -0
- package/templates/skills/ctxr-organize-audit.md +49 -0
- package/templates/skills/ctxr-placement.md +65 -0
- package/templates/skills/ctxr-rollup.md +39 -0
- package/templates/skills/ctxr-session-capture.md +65 -0
- package/templates/skills/ctxr-session-lifecycle.md +39 -0
- package/templates/skills/ctxr-submit.md +26 -0
- package/dist/commands/identity.d.ts +0 -24
- package/dist/commands/identity.d.ts.map +0 -1
- package/dist/commands/identity.js +0 -35
- package/dist/commands/identity.js.map +0 -1
- package/dist/core/checks/identity-checks.d.ts +0 -16
- package/dist/core/checks/identity-checks.d.ts.map +0 -1
- package/dist/core/checks/identity-checks.js +0 -43
- package/dist/core/checks/identity-checks.js.map +0 -1
- package/dist/core/identity.d.ts +0 -46
- package/dist/core/identity.d.ts.map +0 -1
- package/dist/core/identity.js +0 -143
- package/dist/core/identity.js.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Decide where a note lives BEFORE writing it, and say why: the caller wants the reasoning (which layer, why
|
|
2
|
+
this location over its sibling, which visibility, what would promote it) so they can push back — not a
|
|
3
|
+
folder name. Answer the questions in order; stop early only when the content clearly resolves.
|
|
4
|
+
|
|
5
|
+
__LAYER_STEP__
|
|
6
|
+
|
|
7
|
+
## 2. Which location within the layer?
|
|
8
|
+
|
|
9
|
+
- Match on what the content IS. When two sibling locations both seem to fit, name the distinction between
|
|
10
|
+
them (abstract vs concrete; outward-facing vs inward-facing; who-we-are vs how-it-runs) and pick by
|
|
11
|
+
altitude and audience, not by keyword.
|
|
12
|
+
- Read one or two sibling notes in the chosen location and match their shape — frontmatter keys, heading
|
|
13
|
+
style, bullets vs prose — before writing.
|
|
14
|
+
|
|
15
|
+
## 3. Sub-item under an existing location, or a new top-level one?
|
|
16
|
+
|
|
17
|
+
Default to a sub-item until it earns promotion. A near-empty top-level location is its own kind of
|
|
18
|
+
clutter — worse than a slightly crowded existing one. Starting small and promoting later is cheap;
|
|
19
|
+
starting big and hollow is not. Make the promotion trigger a rule, not a vibe, and write it INTO the
|
|
20
|
+
note: "Promote to its own top-level location the moment this holds 3+ distinct notes or topics."
|
|
21
|
+
|
|
22
|
+
## 4. Which visibility?
|
|
23
|
+
|
|
24
|
+
Visibility is a placement input, not an afterthought, because the location sets the visibility default
|
|
25
|
+
(`ctxr note resolve <path>` shows the resolved value and where it came from):
|
|
26
|
+
|
|
27
|
+
- Lean on the location's default; set the visibility field in frontmatter only when you must.
|
|
28
|
+
- Content that bridges two contexts goes to the more restrictive one — this store fails closed to its
|
|
29
|
+
default context (AGENTS.md names it) whenever nothing more specific applies.
|
|
30
|
+
- Visibility can override location: when the visibility the content requires differs from every location
|
|
31
|
+
that fits topically, the topical fit loses.
|
|
32
|
+
- Credentials, full account numbers, and secrets never enter the store — last-4 only.
|
|
33
|
+
|
|
34
|
+
## The collision test — "should A and B be one location?"
|
|
35
|
+
|
|
36
|
+
Before merging two locations, compare their visibility defaults (the placement section shows them). If
|
|
37
|
+
they differ, do NOT merge: a merged location has no safe default, every note in it needs a manual
|
|
38
|
+
override, and the day one is forgotten the fail-closed protection is gone — a structural guard traded for
|
|
39
|
+
human vigilance. If the two genuinely form one continuum with a fuzzy seam, place the boundary
|
|
40
|
+
deliberately and give the overlap ONE bridge note with an explicit visibility value, not a merged drawer.
|
|
41
|
+
A merge request often mis-detects that one side is itself mis-placed; the clean answer can be the
|
|
42
|
+
opposite of merging — split the mis-placed piece out and re-home it. Say so when you see it.
|
|
43
|
+
|
|
44
|
+
## Perishable vs durable
|
|
45
|
+
|
|
46
|
+
One capture often mixes a durable fact with perishable specifics. The durable part (the reusable
|
|
47
|
+
structure, mechanic, or trap) goes into the permanent note. The perishable part (this period's values,
|
|
48
|
+
links, boilerplate) goes into a fenced `contexture:<region>` block you OVERWRITE on each refresh, never
|
|
49
|
+
an accumulating pile of dead entries. Do not ingest a perishable item as source material — that
|
|
50
|
+
manufactures an artifact for the graveyard; capture the durable slice by hand instead.
|
|
51
|
+
|
|
52
|
+
## Wire it in
|
|
53
|
+
|
|
54
|
+
Add a one-line wikilink from the relevant hub note so the new note is not an orphan; `ctxr lint` flags
|
|
55
|
+
orphans and notes without a catalog entry.
|
|
56
|
+
|
|
57
|
+
## Relocating an existing note
|
|
58
|
+
|
|
59
|
+
`ctxr archive <path>` to retire it; a plain tracked `git mv` for an ordinary re-placement. Either way the
|
|
60
|
+
frontmatter — including the visibility field — is left untouched.
|
|
61
|
+
|
|
62
|
+
## Verify
|
|
63
|
+
|
|
64
|
+
`ctxr note resolve <path>` for the visibility; `ctxr lint` for orphans and catalog gaps; `ctxr doctor` for
|
|
65
|
+
anything that blocks a submit.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Regenerate the machine-synthesized current-state region of an entity note (a person, organization,
|
|
2
|
+
product, initiative, or topic hub) from every note that references it. Hand-written content outside the
|
|
3
|
+
fenced region is never touched.
|
|
4
|
+
|
|
5
|
+
Run `ctxr rollup stale` (or `--for <entity>` for one) to find entities worth re-rolling — it lists every
|
|
6
|
+
entity note whose backlinks have moved since its last synthesis, or which has never been rolled up at
|
|
7
|
+
all; use it instead of guessing from memory which entities are out of date.
|
|
8
|
+
|
|
9
|
+
1. Resolve, never create. `<entity>` is a path or a note name; several candidates → list them and ask.
|
|
10
|
+
When a location has more than one plausible hub, prefer the shorter-named, undated one. If no entity
|
|
11
|
+
note exists, STOP and say so — creating it is a separate task, not a side effect of a rollup.
|
|
12
|
+
2. Refuse non-entities. Dated notes, journal entries, meeting notes, generated files, and store
|
|
13
|
+
infrastructure are INPUTS to rollups, not entities. If the name resolves to one of those, stop and ask
|
|
14
|
+
which entity was meant.
|
|
15
|
+
3. Gather: `ctxr rollup gather <entity>` enumerates the candidate sources (notes linking to it). Fewer
|
|
16
|
+
than 3 accepted sources → push back ("only N notes reference this; a rollup would be thin — continue?")
|
|
17
|
+
and do not auto-continue.
|
|
18
|
+
4. Read EVERY accepted source, not a sample — the value of a rollup is total recall over the entity's
|
|
19
|
+
corpus. Per source note its date (filename, frontmatter, then mtime as a last resort), its kind, and
|
|
20
|
+
what it says about THIS entity specifically. For a long source, grep for the entity first.
|
|
21
|
+
5. Synthesize into a file, in this shape — bullets throughout, short clauses; skip any empty subsection
|
|
22
|
+
silently (never "N/A", "none", or "TBD"):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
## Current state
|
|
26
|
+
### Status — one fact per line; what it is, what is happening now, headline problem or metric
|
|
27
|
+
### Recent activity — `YYYY-MM-DD — what happened [[Source]]`, most recent first
|
|
28
|
+
### Open threads — unresolved decisions, blockers, pending actions; each links where it was raised
|
|
29
|
+
### Key people — `[[Person]] — role in this entity's orbit`; only people present in the corpus
|
|
30
|
+
### Sources — every note consulted, sorted; the audit trail
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Provenance rules: every fact traceable to a source note — nothing from memory or earlier conversation;
|
|
34
|
+
no editorializing or recommendations; nothing dated later than today; inline `[[wikilinks]]` when one
|
|
35
|
+
claim has one clear source.
|
|
36
|
+
6. Write: `ctxr rollup write <entity> --content-file <file>` — an idempotent fenced write (`changed: false`
|
|
37
|
+
and a byte-identical file when the content matches; mismatched markers abort with nothing written).
|
|
38
|
+
7. Report: entity path, sources accepted, `changed` or unchanged, and notable findings (e.g. the oldest
|
|
39
|
+
unresolved thread). Anything outside the fence you want to fix is a separate, explicit edit.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
End-of-session capture: propose what the session produced that is durable, write only what is approved.
|
|
2
|
+
The approval gate is what keeps the blast radius low — nothing is written without it.
|
|
3
|
+
|
|
4
|
+
## When
|
|
5
|
+
|
|
6
|
+
Fires on the user's own closing utterance ("done", "ship it", "that's all", a sign-off) or an explicit
|
|
7
|
+
request. A request to open a pull request is also a wrap signal — finish the pull request first, then
|
|
8
|
+
propose (after the LAST one of a batch, not once per request).
|
|
9
|
+
|
|
10
|
+
Anti-triggers — stay silent when: an error, failed test, pending question, or requested follow-up is
|
|
11
|
+
outstanding (the session is not over); the recent turns are dominated by tuning the store or this skill
|
|
12
|
+
itself (nothing is capturable until that lands and gets used); the items would substantially repeat a
|
|
13
|
+
proposal already made this session. The agent's own summary never counts as a signal, nor does a cue
|
|
14
|
+
word inside quoted text, nor a request for a session summary. Zero durable items → silent; never emit
|
|
15
|
+
"scanned, nothing to save".
|
|
16
|
+
|
|
17
|
+
## What is durable
|
|
18
|
+
|
|
19
|
+
Include: decisions with their rationale; net-new concepts; net-new people, organizations, or
|
|
20
|
+
initiatives with context; reusable patterns and playbooks; resolved non-obvious problems worth future
|
|
21
|
+
recall; durable preference or environment facts the user corrected you on.
|
|
22
|
+
|
|
23
|
+
Exclude: tool output, command traces, transient debugging; read-only investigation with no conclusion;
|
|
24
|
+
scratch work that landed on no decision.
|
|
25
|
+
|
|
26
|
+
Unsure → propose (the user can decline).
|
|
27
|
+
|
|
28
|
+
## Proposal — ONE message, empty proposal omitted
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
### Store notes
|
|
32
|
+
- A1 path: <layer>/<location>/<Title>.md
|
|
33
|
+
visibility: <value> — <one-line rationale; `ctxr note resolve` on a sibling shows the default>
|
|
34
|
+
sketch: bullets
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Secret-marker pass: any item whose content looks like a credential gets `⚠ suspected-secret:` on its
|
|
38
|
+
own line with why (patterns worth checking: `sk-`, `ghp_`, `AKIA` + 16 chars, `Bearer …`, a JWT
|
|
39
|
+
`eyJ…`, a URL with a token/key/secret parameter). The marker is a hint; the user is the gate.
|
|
40
|
+
|
|
41
|
+
End with: "Approve by ID (e.g. `A1 A3`) or `skip`." An edit request → re-propose. An ambiguous
|
|
42
|
+
answer → do not proceed.
|
|
43
|
+
|
|
44
|
+
## Apply
|
|
45
|
+
|
|
46
|
+
Write ONLY the approved items to a YAML proposal file, in the shape `ctxr session capture` reads:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
notes:
|
|
50
|
+
- id: A1
|
|
51
|
+
path: <layer>/<location>/<Title>.md
|
|
52
|
+
mode: create # or: append
|
|
53
|
+
visibility: <value> # optional; omit to take the location default
|
|
54
|
+
body: |
|
|
55
|
+
matching the frontmatter and style of the sibling notes (`ctxr-placement` decides the location)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then run `ctxr session capture --proposal <file>`. It validates and writes every item independently —
|
|
59
|
+
one bad path never blocks the rest. These note commits ride the session pull request
|
|
60
|
+
(`ctxr-session-lifecycle`).
|
|
61
|
+
|
|
62
|
+
## Report from actual writes
|
|
63
|
+
|
|
64
|
+
Report exactly what the command reported — wrote / appended / refused (with reason) / skipped, by ID —
|
|
65
|
+
never from the proposal itself. A refused item is surfaced prominently with its reason.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Every write lands via a session worktree and a reviewed pull request; nothing commits to the default
|
|
2
|
+
branch (`__DEFAULT_BRANCH__`). This skill covers what surrounds a session — starting one, re-scanning before
|
|
3
|
+
any plan, resolving conflicts, and sequencing several pull requests. `ctxr-submit` and `ctxr-land` are
|
|
4
|
+
the two verbs at the seams; their steps are not repeated here.
|
|
5
|
+
|
|
6
|
+
## Start
|
|
7
|
+
|
|
8
|
+
`ctxr session start` creates a worktree on a fresh branch off the fetched default branch; work there.
|
|
9
|
+
If you find yourself on the default branch or in the root checkout, stop — `ctxr session list` shows the
|
|
10
|
+
active sessions to work in instead.
|
|
11
|
+
|
|
12
|
+
## Re-scan before any plan
|
|
13
|
+
|
|
14
|
+
Re-scan whenever state may have moved under you — before presenting a plan, before running `ctxr-submit`,
|
|
15
|
+
after any gap in the conversation: `git fetch origin`, `git status --short`, `git diff --stat`,
|
|
16
|
+
`git diff --cached --stat`, `git ls-files --others --exclude-standard`,
|
|
17
|
+
`git log --oneline origin/__DEFAULT_BRANCH__..HEAD`. State moves under you while you work; a plan that grows
|
|
18
|
+
between scans is normal. Name the delta from the previous scan rather than silently folding new work into
|
|
19
|
+
old buckets.
|
|
20
|
+
|
|
21
|
+
## Conflict playbook
|
|
22
|
+
|
|
23
|
+
From the session worktree: `git fetch origin && git rebase origin/__DEFAULT_BRANCH__`. For each conflicting
|
|
24
|
+
file inspect both sides (`git show`), understand what each represents, and produce a version that keeps
|
|
25
|
+
both meanings — never take one side blindly. `git add <file>`, `GIT_EDITOR=true git rebase --continue`,
|
|
26
|
+
then `git push --force-with-lease origin <branch>` (aborts if the remote moved underneath you). Never
|
|
27
|
+
resolve on the default branch.
|
|
28
|
+
|
|
29
|
+
## Multi-PR sequencing
|
|
30
|
+
|
|
31
|
+
Parallel when the units are file-disjoint — one `ctxr session start` per unit. Sequential when files
|
|
32
|
+
overlap: wait for the merge (`ctxr-land`), then start the next session off the updated default branch.
|
|
33
|
+
Worktrees isolate directories, not logical writes: a hot file every session appends to collides at merge
|
|
34
|
+
time regardless — sequence those.
|
|
35
|
+
|
|
36
|
+
## Reclaiming
|
|
37
|
+
|
|
38
|
+
`ctxr session reap` removes merged, clean worktrees (or use `ctxr-land`'s `--reap`); `ctxr session abandon
|
|
39
|
+
<branch>` discards work and needs an explicit go. Never claim cleanup happened without having run one.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
The submit half of the session lifecycle — everything up to and including opening the pull request.
|
|
2
|
+
`ctxr-land` is the other half, after review; `ctxr-session-lifecycle` covers what surrounds both (starting
|
|
3
|
+
a session, the conflict playbook, sequencing several pull requests) and is not repeated here.
|
|
4
|
+
|
|
5
|
+
1. Re-scan (mandatory — never replay a plan from an earlier snapshot): `git fetch origin`,
|
|
6
|
+
`git status --short`, `git diff --stat`, `git diff --cached --stat`,
|
|
7
|
+
`git ls-files --others --exclude-standard`, `git log --oneline origin/__DEFAULT_BRANCH__..HEAD`. State moves
|
|
8
|
+
under you while you work; name the delta from the previous scan rather than silently folding new work
|
|
9
|
+
into old buckets.
|
|
10
|
+
2. Capture pass: run `ctxr-session-capture` exactly once here — closing a session is itself a capture
|
|
11
|
+
trigger; do not fire it again after submitting.
|
|
12
|
+
3. Stage surgically: `git add <paths>`, never `git add -A`; confirm with `git status --short` that the
|
|
13
|
+
staged set matches the intended unit. Derived artifacts under the cache paths never stage.
|
|
14
|
+
4. One coherent unit per pull request. If the session produced two disjoint units, say so and ask
|
|
15
|
+
whether to split.
|
|
16
|
+
5. Fire gate: `ctxr session submit` commits, pushes, and opens the pull request — an external side
|
|
17
|
+
effect, and plan consent is not fire consent. Present the branch, the title, and what rides it; wait
|
|
18
|
+
for an explicit go before running it.
|
|
19
|
+
6. Run: `ctxr session submit --branch "<name>" --title "<title>" --body "<why / what changed /
|
|
20
|
+
verification / follow-ups>"`. Name a real branch with `--branch` — never let a generated name reach
|
|
21
|
+
the forge. It runs the full store validation first; fix a failure, never bypass it. If no forge is
|
|
22
|
+
reachable it still pushes and prints the manual pull-request instructions.
|
|
23
|
+
7. Verify before any retry: a transport error can arrive AFTER the push or the pull-request open already
|
|
24
|
+
succeeded. Before retrying anything, `git ls-remote origin <branch>` and the forge's pull-request list
|
|
25
|
+
for the branch — never replay a push or a pull-request open blindly.
|
|
26
|
+
8. Hand off: report the pull request, then point at `ctxr-land` for after review.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { CommandOutcome, CommandRequires } from '../core/command.js';
|
|
2
|
-
import type { Store } from '../core/store.js';
|
|
3
|
-
export declare const requires: CommandRequires;
|
|
4
|
-
export interface IdentityEditData {
|
|
5
|
-
path: string;
|
|
6
|
-
entries: number;
|
|
7
|
-
}
|
|
8
|
-
export interface IdentityAddFlags {
|
|
9
|
-
file: string;
|
|
10
|
-
text: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function executeAdd(store: Store, flags: IdentityAddFlags): Promise<CommandOutcome<IdentityEditData>>;
|
|
13
|
-
export interface IdentityReplaceFlags {
|
|
14
|
-
file: string;
|
|
15
|
-
match: string;
|
|
16
|
-
text: string;
|
|
17
|
-
}
|
|
18
|
-
export declare function executeReplace(store: Store, flags: IdentityReplaceFlags): Promise<CommandOutcome<IdentityEditData>>;
|
|
19
|
-
export interface IdentityRemoveFlags {
|
|
20
|
-
file: string;
|
|
21
|
-
match: string;
|
|
22
|
-
}
|
|
23
|
-
export declare function executeRemove(store: Store, flags: IdentityRemoveFlags): Promise<CommandOutcome<IdentityEditData>>;
|
|
24
|
-
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/commands/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAU1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,eAAO,MAAM,QAAQ,EAAE,eAAuC,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAmBD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAIjH;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAIzH;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAIvH"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ExitCode } from '../core/exit-codes.js';
|
|
2
|
-
import { UnknownIdentityRoleError } from '../core/errors.js';
|
|
3
|
-
import { addIdentityEntry, IDENTITY_ROLES, removeIdentityEntry, replaceIdentityEntry, } from '../core/identity.js';
|
|
4
|
-
export const requires = { store: 'required' };
|
|
5
|
-
function checkRole(role) {
|
|
6
|
-
if (!IDENTITY_ROLES.includes(role)) {
|
|
7
|
-
throw new UnknownIdentityRoleError(role, IDENTITY_ROLES);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
function outcome(store, result, humanSummary) {
|
|
11
|
-
return {
|
|
12
|
-
exitCode: ExitCode.Ok,
|
|
13
|
-
data: result,
|
|
14
|
-
findings: [],
|
|
15
|
-
humanSummary,
|
|
16
|
-
storeRoot: store.root,
|
|
17
|
-
schemaVersion: store.config.schema_version,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export async function executeAdd(store, flags) {
|
|
21
|
-
checkRole(flags.file);
|
|
22
|
-
const result = await addIdentityEntry(store.root, store.config, flags.file, flags.text);
|
|
23
|
-
return outcome(store, result, `Added an entry to "${result.path}" (${result.entries} total).`);
|
|
24
|
-
}
|
|
25
|
-
export async function executeReplace(store, flags) {
|
|
26
|
-
checkRole(flags.file);
|
|
27
|
-
const result = await replaceIdentityEntry(store.root, store.config, flags.file, flags.match, flags.text);
|
|
28
|
-
return outcome(store, result, `Replaced the entry matching "${flags.match}" in "${result.path}" (${result.entries} total).`);
|
|
29
|
-
}
|
|
30
|
-
export async function executeRemove(store, flags) {
|
|
31
|
-
checkRole(flags.file);
|
|
32
|
-
const result = await removeIdentityEntry(store.root, store.config, flags.file, flags.match);
|
|
33
|
-
return outcome(store, result, `Removed the entry matching "${flags.match}" from "${result.path}" (${result.entries} total).`);
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=identity.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/commands/identity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,GAErB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,CAAC,MAAM,QAAQ,GAAoB,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAO/D,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAE,cAAoC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,wBAAwB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,KAAY,EAAE,MAAyC,EAAE,YAAoB;IAC5F,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,EAAE;QACrB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,EAAE;QACZ,YAAY;QACZ,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;KAC3C,CAAC;AACJ,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAY,EAAE,KAAuB;IACpE,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACxF,OAAO,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,sBAAsB,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AACjG,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAY,EAAE,KAA2B;IAC5E,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACzG,OAAO,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,gCAAgC,KAAK,CAAC,KAAK,SAAS,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAC/H,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAY,EAAE,KAA0B;IAC1E,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5F,OAAO,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,+BAA+B,KAAK,CAAC,KAAK,WAAW,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAChI,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* agent-identity spec: "Identity files... SHALL live under a path declared
|
|
3
|
-
* in the store's retrieval exclusion configuration." Catalog and graph
|
|
4
|
-
* already can't surface anything under an excluded prefix (they're both
|
|
5
|
-
* built from listNotes()) — this check guards the one way that guarantee
|
|
6
|
-
* could quietly break: an operator editing config.retrieval.exclude_paths
|
|
7
|
-
* and dropping an identity path from it.
|
|
8
|
-
*
|
|
9
|
-
* session-capture-command spec (D3): checked against each ROLE'S RESOLVED
|
|
10
|
-
* path — not the identity directory alone — so a role relocated outside
|
|
11
|
-
* `identity.path` (e.g. into a directory a harness runtime links into)
|
|
12
|
-
* still cannot leak into retrieval.
|
|
13
|
-
*/
|
|
14
|
-
export declare const identityExclusionCheck: import("./types.js").CheckDefinition;
|
|
15
|
-
export declare const IDENTITY_CHECKS: import("./types.js").CheckDefinition[];
|
|
16
|
-
//# sourceMappingURL=identity-checks.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity-checks.d.ts","sourceRoot":"","sources":["../../../src/core/checks/identity-checks.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,sCAyBjC,CAAC;AAEH,eAAO,MAAM,eAAe,wCAA2B,CAAC"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { IDENTITY_ROLES, identityFilePath } from '../identity.js';
|
|
2
|
-
import { defineCheck } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* agent-identity spec: "Identity files... SHALL live under a path declared
|
|
5
|
-
* in the store's retrieval exclusion configuration." Catalog and graph
|
|
6
|
-
* already can't surface anything under an excluded prefix (they're both
|
|
7
|
-
* built from listNotes()) — this check guards the one way that guarantee
|
|
8
|
-
* could quietly break: an operator editing config.retrieval.exclude_paths
|
|
9
|
-
* and dropping an identity path from it.
|
|
10
|
-
*
|
|
11
|
-
* session-capture-command spec (D3): checked against each ROLE'S RESOLVED
|
|
12
|
-
* path — not the identity directory alone — so a role relocated outside
|
|
13
|
-
* `identity.path` (e.g. into a directory a harness runtime links into)
|
|
14
|
-
* still cannot leak into retrieval.
|
|
15
|
-
*/
|
|
16
|
-
export const identityExclusionCheck = defineCheck({
|
|
17
|
-
id: 'identity.excluded_from_retrieval',
|
|
18
|
-
title: 'Every identity role\'s resolved path is excluded from retrieval',
|
|
19
|
-
severity: 'invariant',
|
|
20
|
-
capability: 'agent-identity',
|
|
21
|
-
scopes: ['store'],
|
|
22
|
-
async run(ctx) {
|
|
23
|
-
const findings = [];
|
|
24
|
-
for (const role of IDENTITY_ROLES) {
|
|
25
|
-
const resolvedPath = identityFilePath(ctx.config, role);
|
|
26
|
-
const covered = ctx.config.retrieval.exclude_paths.some((prefix) => {
|
|
27
|
-
const trimmed = prefix.replace(/\/+$/, '');
|
|
28
|
-
return resolvedPath === trimmed || resolvedPath.startsWith(`${trimmed}/`);
|
|
29
|
-
});
|
|
30
|
-
if (!covered) {
|
|
31
|
-
findings.push({
|
|
32
|
-
code: 'identity.not_excluded',
|
|
33
|
-
severity: 'error',
|
|
34
|
-
message: `The ${role} identity file "${resolvedPath}" is not covered by retrieval.exclude_paths — it could leak into the catalog or graph.`,
|
|
35
|
-
subject: resolvedPath,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return { status: findings.length > 0 ? 'fail' : 'pass', findings };
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
export const IDENTITY_CHECKS = [identityExclusionCheck];
|
|
43
|
-
//# sourceMappingURL=identity-checks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity-checks.js","sourceRoot":"","sources":["../../../src/core/checks/identity-checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;IAChD,EAAE,EAAE,kCAAkC;IACtC,KAAK,EAAE,iEAAiE;IACxE,QAAQ,EAAE,WAAW;IACrB,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,CAAC,GAAG;QACX,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACjE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC3C,OAAO,YAAY,KAAK,OAAO,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;YAC5E,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,uBAAuB;oBAC7B,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,OAAO,IAAI,mBAAmB,YAAY,wFAAwF;oBAC3I,OAAO,EAAE,YAAY;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACrE,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,sBAAsB,CAAC,CAAC"}
|
package/dist/core/identity.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { StoreConfig } from '../config/schema.js';
|
|
2
|
-
/**
|
|
3
|
-
* agent-identity spec: three canonical, harness-neutral roles — posture,
|
|
4
|
-
* durable world facts, durable user facts — kept distinct from retrievable
|
|
5
|
-
* knowledge (excluded from every retrieval leg via each resolved path being
|
|
6
|
-
* covered by config.retrieval.exclude_paths). A harness's own
|
|
7
|
-
* identity-injection adapter decides how these reach a running agent; core
|
|
8
|
-
* never reads their content itself.
|
|
9
|
-
*
|
|
10
|
-
* session-capture-command spec (D3): a role's path is CONFIGURABLE
|
|
11
|
-
* (`identity.files`) — a store whose runtime keeps its memory elsewhere
|
|
12
|
-
* points the role there, and every consumer below resolves through
|
|
13
|
-
* `identityFilePath`/`identityFilePaths` rather than assuming the canonical
|
|
14
|
-
* layout, so the relocated file IS the mechanism, not a copy of it.
|
|
15
|
-
*/
|
|
16
|
-
export declare const IDENTITY_ROLES: readonly ['posture', 'world-facts', 'user-facts'];
|
|
17
|
-
export type IdentityRole = (typeof IDENTITY_ROLES)[number];
|
|
18
|
-
/** The canonical filename each role resolves to under `identity.path` when no override is configured. */
|
|
19
|
-
export declare const IDENTITY_FILES: Record<IdentityRole, string>;
|
|
20
|
-
/** The store-relative path this role resolves to: its configured binding, or its canonical file under `identity.path`. */
|
|
21
|
-
export declare function identityFilePath(config: StoreConfig, role: IdentityRole): string;
|
|
22
|
-
export declare function identityFilePaths(config: StoreConfig): string[];
|
|
23
|
-
/** Creates any of the three canonical identity files that don't exist yet — never overwrites an existing one. */
|
|
24
|
-
export declare function ensureIdentityFiles(root: string, config: StoreConfig): Promise<string[]>;
|
|
25
|
-
/**
|
|
26
|
-
* session-capture-command spec (D4): an entry is the text between two
|
|
27
|
-
* delimiter lines. The default delimiter ('') means an empty (blank) line,
|
|
28
|
-
* so paragraphs are entries and a heading-sectioned file's headings simply
|
|
29
|
-
* become part of the entry that follows them — no section vocabulary, no
|
|
30
|
-
* format detection.
|
|
31
|
-
*/
|
|
32
|
-
export declare function splitEntries(text: string, delimiter: string): string[];
|
|
33
|
-
export declare function joinEntries(entries: readonly string[], delimiter: string): string;
|
|
34
|
-
export interface IdentityEntryResult {
|
|
35
|
-
/** The role's resolved, store-relative path. */
|
|
36
|
-
path: string;
|
|
37
|
-
/** The entry count after the edit. */
|
|
38
|
-
entries: number;
|
|
39
|
-
}
|
|
40
|
-
/** Appends a new entry at the end of the role's file, creating it (with no other entries) if it doesn't exist yet. */
|
|
41
|
-
export declare function addIdentityEntry(root: string, config: StoreConfig, role: IdentityRole, text: string): Promise<IdentityEntryResult>;
|
|
42
|
-
/** Replaces the single entry containing `match`; refuses, writing nothing, when zero or several entries match. */
|
|
43
|
-
export declare function replaceIdentityEntry(root: string, config: StoreConfig, role: IdentityRole, match: string, text: string): Promise<IdentityEntryResult>;
|
|
44
|
-
/** Removes the single entry containing `match`; refuses, writing nothing, when zero or several entries match. */
|
|
45
|
-
export declare function removeIdentityEntry(root: string, config: StoreConfig, role: IdentityRole, match: string): Promise<IdentityEntryResult>;
|
|
46
|
-
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/core/identity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,YAAI,SAAS,EAAE,aAAa,EAAE,YAAY,CAAU,CAAC;AAChF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,yGAAyG;AACzG,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAIvD,CAAC;AAQF,0HAA0H;AAC1H,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAIhF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAE/D;AAED,iHAAiH;AACjH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBtE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIjF;AAwBD,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sHAAsH;AACtH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAQxI;AAED,kHAAkH;AAClH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAQ9B;AAED,iHAAiH;AACjH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,mBAAmB,CAAC,CAQ9B"}
|
package/dist/core/identity.js
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readFile } from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { IdentityEntryMatchError } from './errors.js';
|
|
5
|
-
import { writeFileAtomic } from './fs/atomic.js';
|
|
6
|
-
/**
|
|
7
|
-
* agent-identity spec: three canonical, harness-neutral roles — posture,
|
|
8
|
-
* durable world facts, durable user facts — kept distinct from retrievable
|
|
9
|
-
* knowledge (excluded from every retrieval leg via each resolved path being
|
|
10
|
-
* covered by config.retrieval.exclude_paths). A harness's own
|
|
11
|
-
* identity-injection adapter decides how these reach a running agent; core
|
|
12
|
-
* never reads their content itself.
|
|
13
|
-
*
|
|
14
|
-
* session-capture-command spec (D3): a role's path is CONFIGURABLE
|
|
15
|
-
* (`identity.files`) — a store whose runtime keeps its memory elsewhere
|
|
16
|
-
* points the role there, and every consumer below resolves through
|
|
17
|
-
* `identityFilePath`/`identityFilePaths` rather than assuming the canonical
|
|
18
|
-
* layout, so the relocated file IS the mechanism, not a copy of it.
|
|
19
|
-
*/
|
|
20
|
-
export const IDENTITY_ROLES = ['posture', 'world-facts', 'user-facts'];
|
|
21
|
-
/** The canonical filename each role resolves to under `identity.path` when no override is configured. */
|
|
22
|
-
export const IDENTITY_FILES = {
|
|
23
|
-
posture: 'posture.md',
|
|
24
|
-
'world-facts': 'world-facts.md',
|
|
25
|
-
'user-facts': 'user-facts.md',
|
|
26
|
-
};
|
|
27
|
-
const TEMPLATES = {
|
|
28
|
-
posture: '# Agent posture\n\nHow an agent should approach work in this store: tone, defaults, things to always or never do.\n',
|
|
29
|
-
'world-facts': '# Durable world facts\n\nFacts about the world (not the user) worth carrying into every session.\n',
|
|
30
|
-
'user-facts': '# Durable user facts\n\nFacts about the user worth carrying into every session.\n',
|
|
31
|
-
};
|
|
32
|
-
/** The store-relative path this role resolves to: its configured binding, or its canonical file under `identity.path`. */
|
|
33
|
-
export function identityFilePath(config, role) {
|
|
34
|
-
const bound = config.identity.files[role];
|
|
35
|
-
const relative = bound ?? path.join(config.identity.path, IDENTITY_FILES[role]);
|
|
36
|
-
return relative.split(path.sep).join('/');
|
|
37
|
-
}
|
|
38
|
-
export function identityFilePaths(config) {
|
|
39
|
-
return IDENTITY_ROLES.map((role) => identityFilePath(config, role));
|
|
40
|
-
}
|
|
41
|
-
/** Creates any of the three canonical identity files that don't exist yet — never overwrites an existing one. */
|
|
42
|
-
export async function ensureIdentityFiles(root, config) {
|
|
43
|
-
const created = [];
|
|
44
|
-
for (const role of IDENTITY_ROLES) {
|
|
45
|
-
const relativePath = identityFilePath(config, role);
|
|
46
|
-
const absolutePath = path.join(root, relativePath);
|
|
47
|
-
if (!existsSync(absolutePath)) {
|
|
48
|
-
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
49
|
-
await writeFileAtomic(absolutePath, TEMPLATES[role]);
|
|
50
|
-
created.push(relativePath);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return created;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* session-capture-command spec (D4): an entry is the text between two
|
|
57
|
-
* delimiter lines. The default delimiter ('') means an empty (blank) line,
|
|
58
|
-
* so paragraphs are entries and a heading-sectioned file's headings simply
|
|
59
|
-
* become part of the entry that follows them — no section vocabulary, no
|
|
60
|
-
* format detection.
|
|
61
|
-
*/
|
|
62
|
-
export function splitEntries(text, delimiter) {
|
|
63
|
-
const isDelimiterLine = (line) => (delimiter === '' ? line.trim() === '' : line === delimiter);
|
|
64
|
-
const entries = [];
|
|
65
|
-
let current = [];
|
|
66
|
-
// A single trailing newline is a file-formatting convention, not a blank final line — for a
|
|
67
|
-
// custom delimiter it would otherwise leak into the last entry as trailing junk.
|
|
68
|
-
const trimmed = text.endsWith('\n') ? text.slice(0, -1) : text;
|
|
69
|
-
for (const line of trimmed.split('\n')) {
|
|
70
|
-
if (isDelimiterLine(line)) {
|
|
71
|
-
if (current.length > 0) {
|
|
72
|
-
entries.push(current.join('\n'));
|
|
73
|
-
current = [];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
current.push(line);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (current.length > 0)
|
|
81
|
-
entries.push(current.join('\n'));
|
|
82
|
-
return entries;
|
|
83
|
-
}
|
|
84
|
-
export function joinEntries(entries, delimiter) {
|
|
85
|
-
if (entries.length === 0)
|
|
86
|
-
return '';
|
|
87
|
-
const separator = delimiter === '' ? '\n\n' : `\n${delimiter}\n`;
|
|
88
|
-
return `${entries.join(separator)}\n`;
|
|
89
|
-
}
|
|
90
|
-
async function readEntries(absolutePath, delimiter) {
|
|
91
|
-
let text;
|
|
92
|
-
try {
|
|
93
|
-
text = await readFile(absolutePath, 'utf8');
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
if (err.code === 'ENOENT')
|
|
97
|
-
return [];
|
|
98
|
-
throw err;
|
|
99
|
-
}
|
|
100
|
-
return splitEntries(text, delimiter);
|
|
101
|
-
}
|
|
102
|
-
function findUniqueMatch(entries, match, filePath) {
|
|
103
|
-
const indices = entries.reduce((acc, entry, index) => {
|
|
104
|
-
if (entry.includes(match))
|
|
105
|
-
acc.push(index);
|
|
106
|
-
return acc;
|
|
107
|
-
}, []);
|
|
108
|
-
if (indices.length !== 1) {
|
|
109
|
-
throw new IdentityEntryMatchError(filePath, match, indices.length);
|
|
110
|
-
}
|
|
111
|
-
return indices[0];
|
|
112
|
-
}
|
|
113
|
-
/** Appends a new entry at the end of the role's file, creating it (with no other entries) if it doesn't exist yet. */
|
|
114
|
-
export async function addIdentityEntry(root, config, role, text) {
|
|
115
|
-
const relativePath = identityFilePath(config, role);
|
|
116
|
-
const absolutePath = path.join(root, relativePath);
|
|
117
|
-
const entries = await readEntries(absolutePath, config.identity.entry_delimiter);
|
|
118
|
-
entries.push(text);
|
|
119
|
-
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
120
|
-
await writeFileAtomic(absolutePath, joinEntries(entries, config.identity.entry_delimiter));
|
|
121
|
-
return { path: relativePath, entries: entries.length };
|
|
122
|
-
}
|
|
123
|
-
/** Replaces the single entry containing `match`; refuses, writing nothing, when zero or several entries match. */
|
|
124
|
-
export async function replaceIdentityEntry(root, config, role, match, text) {
|
|
125
|
-
const relativePath = identityFilePath(config, role);
|
|
126
|
-
const absolutePath = path.join(root, relativePath);
|
|
127
|
-
const entries = await readEntries(absolutePath, config.identity.entry_delimiter);
|
|
128
|
-
const index = findUniqueMatch(entries, match, relativePath);
|
|
129
|
-
entries[index] = text;
|
|
130
|
-
await writeFileAtomic(absolutePath, joinEntries(entries, config.identity.entry_delimiter));
|
|
131
|
-
return { path: relativePath, entries: entries.length };
|
|
132
|
-
}
|
|
133
|
-
/** Removes the single entry containing `match`; refuses, writing nothing, when zero or several entries match. */
|
|
134
|
-
export async function removeIdentityEntry(root, config, role, match) {
|
|
135
|
-
const relativePath = identityFilePath(config, role);
|
|
136
|
-
const absolutePath = path.join(root, relativePath);
|
|
137
|
-
const entries = await readEntries(absolutePath, config.identity.entry_delimiter);
|
|
138
|
-
const index = findUniqueMatch(entries, match, relativePath);
|
|
139
|
-
entries.splice(index, 1);
|
|
140
|
-
await writeFileAtomic(absolutePath, joinEntries(entries, config.identity.entry_delimiter));
|
|
141
|
-
return { path: relativePath, entries: entries.length };
|
|
142
|
-
}
|
|
143
|
-
//# sourceMappingURL=identity.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/core/identity.ts"],"names":[],"mappings":"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"}
|