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
42 changes: 42 additions & 0 deletions scripts/sync-skills.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,39 @@ IGNORE_RE='^(\.git|node_modules|\.agents|\.github|scripts|docs|skills|\.husky)$'
# Bash 3.2 (default on macOS) does not support associative arrays.
declare -a SKILL_NAMES=()
declare -a SKILL_PATHS=()
declare -a SHARED_NAMES=()
declare -a SHARED_PATHS=()

echo "[sync-skills] scanning plugin skill directories..."

while IFS= read -r plugin_dir; do
skills_root="$plugin_dir/skills"
[[ -d "$skills_root" ]] || continue

references_dir="$skills_root/references"
if [[ -d "$references_dir" ]]; then
existing_src=""
for i in "${!SHARED_NAMES[@]}"; do
if [[ "${SHARED_NAMES[$i]}" == "references" ]]; then
existing_src="${SHARED_PATHS[$i]}"
break
fi
done

if [[ -n "$existing_src" && "$existing_src" != "$references_dir" ]]; then
echo "[sync-skills] duplicate shared references directory found:" >&2
echo " - $existing_src" >&2
echo " - $references_dir" >&2
echo "Merge or rename one of them before syncing." >&2
exit 1
fi

if [[ -z "$existing_src" ]]; then
SHARED_NAMES+=("references")
SHARED_PATHS+=("$references_dir")
fi
fi

while IFS= read -r skill_dir; do
[[ -f "$skill_dir/SKILL.md" ]] || continue
skill_name="$(basename "$skill_dir")"
Expand Down Expand Up @@ -67,6 +93,12 @@ while IFS= read -r existing; do
break
fi
done
for i in "${!SHARED_NAMES[@]}"; do
if [[ "${SHARED_NAMES[$i]}" == "$name" ]]; then
found=1
break
fi
done
if [[ "$found" -eq 0 ]]; then
echo "[sync-skills] removing stale skill: $name"
rm -rf "$existing"
Expand All @@ -83,4 +115,14 @@ for i in "${!SKILL_NAMES[@]}"; do
echo "[sync-skills] synced: $name"
done

# Sync shared skill asset directories such as references/.
for i in "${!SHARED_NAMES[@]}"; do
name="${SHARED_NAMES[$i]}"
src="${SHARED_PATHS[$i]}"
dst="$DEST/$name"
mkdir -p "$dst"
rsync -a --delete --exclude node_modules/ --exclude .env "$src/" "$dst/"
echo "[sync-skills] synced shared: $name"
done

echo "[sync-skills] done"
256 changes: 256 additions & 0 deletions skills/references/portent-knowledge-base-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# Portent Knowledge Base Spec

Portent is an opinionated model for personal and work knowledge bases. Use it to
organize information as typed objects connected by explicit relationships, with
a simple lifecycle.

## Core Rules

- Model reality first. Do not use folders as the main source of meaning.
- Every durable object should answer: what is this?
- Every organized object should answer: what will this be useful for?
- Prefer Portent defaults before creating custom types or relationships.
- Keep captured material easy to record, then organize it later.
- Keep archived material searchable, but hidden from active work by default.

## Object Shape

Every Portent object should have:

- `title`: human-readable name
- `type`: one of the Portent types
- `content`: readable body text
- lifecycle metadata: organized/archived state
- relationships: explicit links to other objects

In Markdown, store metadata in frontmatter and use wikilinks for local
references.

Example:

```yaml
---
type: Event
organized: true
archived: false
belongs_to: '[[Launch Portent v0.1]]'
related_to:
- '[[Alice Example]]'
- '[[Knowledge graphs]]'
---
```

## Types

Portent has eight default types.

### Project

A bounded effort that produces an output.

Use for work that:

- has a beginning and an end
- cannot be completed in one sitting
- has success criteria or a definition of done
- advances one or more responsibilities

Projects are about outputs.

### Operation

Recurring work that can usually be completed in one sitting.

Use for repeatable actions such as reviews, checks, publishing routines,
maintenance routines, or recurring admin work.

Operations usually belong to a responsibility, but can also support a project.

### Responsibility

A long-running area of accountability.

Use for outcomes that should be maintained or improved over time.

Responsibilities usually:

- do not have a natural end date
- are measured by indicators, metrics, KPIs, or standards
- collect projects and operations that improve or maintain the outcome

Responsibilities are about outcomes.

### Task

One-off work that can usually be completed in one sitting.

Tasks are part of Portent's worldview, but they do not have to live inside the
knowledge base. They can live in Todoist, Linear, GitHub Issues, or another task
tool as long as they can be related back to Portent objects.

### Event

Something that happened and should be retained in long-term memory.

Use for meetings, conversations, decisions, achievements, incidents, personal
events, external changes, or historical records.

Events often belong to a project or responsibility and are related to people and
topics.

### Note

A durable knowledge artifact.

Use for documents, resources, references, research summaries, decision records,
checklists, tools, or any material that helps understand or advance another
object.

### Topic

An area of interest or conceptual lens.

Use for knowledge that should be collected without implying ownership,
completion, or performance expectations.

Topics are useful for grouping notes and events across projects and
responsibilities.

### Person

A real-world person or, when useful, an AI agent treated as an actor.

Use for contacts, collaborators, customers, vendors, authors, attendees,
decision makers, stakeholders, or agents.

## Type Groups

PORT types are actionable:

- Project
- Operation
- Responsibility
- Task

ENTP types are non-actionable records:

- Event
- Note
- Topic
- Person

Use PORT when the object is something to do or operate. Use ENTP when the object
records what happened, what is known, what is interesting, or who is involved.

## Relationships

Portent uses two default relationship types.

### belongs_to

Primary context, ownership, or composition.

Use when an object has a main parent or main operating context.

Examples:

- a task belongs to a project
- an operation belongs to a responsibility
- an event belongs to a project
- a note belongs to the project or responsibility it primarily supports

Most objects should have at most one primary parent for a given organizing
purpose.

Inverse: `has`, `contains`, or `children`.

### related_to

Secondary usefulness or association without ownership.

Use for many-to-many links and supporting context.

Examples:

- a meeting event is related to attendees
- a note is related to multiple topics
- a project is related to a responsibility it improves
- a person is related to projects they influence

Inverse: `referred_by`, `linked_from`, backlinks, or related items.

## Lifecycle

Every object can move through three lifecycle states.

### Captured

Recorded but not yet actionable.

Captured objects may have unclear titles, missing types, missing relationships,
or incomplete context. They belong in an inbox or temporary surface.

Capture optimistically.

### Organized

Structured enough to be useful later.

An object is organized when it has:

- a clear title
- a type
- enough relationships to explain future use

Organize pessimistically. If a captured object cannot attach to a project,
responsibility, operation, or topic, consider deleting it.

### Archived

No longer useful in active work, but still useful as memory.

Archived objects should remain searchable and referenceable, but hidden from
active views by default.

Archive instead of deleting when the object still has historical, audit, or
reference value.

## Lifecycle Fields

Use any representation that preserves organized and archived state.

Separate fields:

```yaml
organized: true
archived: false
```

Single status field:

```yaml
status: organized
```

Allowed statuses should map to:

- captured
- organized
- archived

## Extension Rules

Portent can be extended, but defaults should come first.

Add a custom type only when:

- the object has different behavior, templates, relationships, or workflows
- a property on an existing type is not enough

Add a custom relationship only when:

- `belongs_to` or `related_to` hides important meaning
- the relationship needs distinct behavior or validation

Prefer properties before root types. Prefer default relationships before custom
relationships.
12 changes: 11 additions & 1 deletion skills/vault-compact/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Reduce repeated thinking across the active vault. This skill is for semantic
overlap, not just exact duplicate text: compact passages when they do the same
job in the knowledge system.

Portent reference: `../references/portent-knowledge-base-spec.md`.

Prefer compaction when files represent the same Portent object or repeat the
same claim for the same object. Do not merge merely because objects share a
Topic. Preserve `belongs_to` and `related_to` metadata when absorbing files.

## Command

```
Expand Down Expand Up @@ -83,7 +89,8 @@ detail.
- `qmd search` for exact phrases, titles, URLs, and aliases
- `rg` for migration residue, raw strings, and link rewrites
5. Build candidate groups by canonical topic, project, idea, source, or claim.
6. Classify each group by confidence and compaction action.
6. Classify each group by Portent object identity, confidence, and compaction
action.
7. In `report` mode, stop after a compact report.
8. In `apply-safe` mode, apply only local, high-confidence edits inside one file
or obvious link substitutions to canonical concept notes.
Expand Down Expand Up @@ -116,6 +123,7 @@ detail.

- Same source URL or same clipped source identity.
- Same title, slug, project, or idea identity.
- Same Portent object identity or same claim serving the same object.
- One note explicitly continues, replaces, or supersedes another.
- Passages make the same claim with compatible meaning.
- Differences are examples, wording, or ordering, not substance.
Expand Down Expand Up @@ -152,6 +160,8 @@ Low-confidence items should remain separate; propose links only when useful.
identity and do not absorb complete external text into owned notes.
- Repeated source summaries should cite complete source records under
`raw/processed/YYYY-MM-DD/` when used for synthesis.
- Preserve `belongs_to` and `related_to` metadata when compacting or absorbing
notes.

### Ideas

Expand Down
8 changes: 8 additions & 0 deletions skills/vault-concepts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ description: Maintain canonical concept pages and surface recurring themes
Promote recurring themes into canonical concept pages and keep concept
navigation coherent.

Portent reference: `../references/portent-knowledge-base-spec.md`.

## Purpose

Maintain `notes/concepts/` as the durable vocabulary of your vault. Surface
cross-domain patterns and decide when transience deserves permanence.

Concept pages are Portent `Topic` objects. New recurring-theme pages should use
`type: Topic`, `status: organized`, and explicit `related_to` links to the
Projects, Notes, Events, or Responsibilities they help explain.

## Parameters

- `--topic TOPIC` focus on specific theme (optional)
Expand All @@ -25,6 +31,7 @@ Only create new concept pages when ALL are true:
- Theme appears in 3+ unrelated notes
- Evidence is concrete and linkable
- Destination concept is clear
- Destination Topic object is clear
- Change is additive and low-risk

## Workflow
Expand Down Expand Up @@ -90,6 +97,7 @@ Return:

- Concepts created/updated/proposed (with confidence)
- Concepts merged/proposed for merge
- Topic object metadata added or updated
- Evidence links used
- Reference normalization performed
- Index/log update status
Expand Down
Loading
Loading