Documentation
¶
Overview ¶
Package command holds domain command structs — write intent. Commands are dispatched to handlers for execution; results flow back through optional callback functions on the command struct (handlers themselves return only errors so write paths and read paths stay distinct).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Attachment ¶
type Attachment struct {
Source string // file path, or "-" for stdin
Target string // destination filename inside the attachment directory
Data []byte // populated when Source == "-"
}
Attachment describes a file to attach to a new entry. For stdin attachments the source is "-" and Data holds the already-read bytes — the CLI layer materializes stdin before constructing the command so the handler operates on a self-contained value.
type BuildIndexCmd ¶ added in v0.4.0
type BuildIndexCmd struct {
// Force re-embeds every entry even when the manifest already records
// an up-to-date row set.
Force bool
// OnBatchStart is called before each embedding round-trip with the entry
// IDs in that batch. Optional; intended for progress reporting — the
// determinate bar advances per batch as embedding begins, rather than in
// one jump at the end.
OnBatchStart func(entryIDs []string)
// OnEntryIndexed is called once per entry after its rows are upserted.
// Optional; intended for CLI progress output.
OnEntryIndexed func(entryID string, chunkCount int)
// OnEntrySkipped is called for entries whose manifest record matches
// the current state (skipped under Force=false). Optional.
OnEntrySkipped func(entryID string)
// OnComplete is called once after the build finishes successfully,
// with the totals indexed and skipped. Optional.
OnComplete func(indexed, skipped int)
}
BuildIndexCmd warms up the search index by chunking, embedding, and upserting every entry on disk. Existing rows for re-indexed entries are dropped before the new rows land. The command is idempotent — re-running Build over an up-to-date index is a no-op when Force is false (entries whose hash and fingerprint match the manifest are skipped); Force=true re-embeds and re-upserts everything regardless.
type BumpMinimumVersionCmd ¶ added in v0.5.1
type BumpMinimumVersionCmd struct {
// SDDDir is the absolute path to the .sdd/ directory.
SDDDir string
// BinaryVersion is the running sdd binary's version. Must be a
// released semver — dev builds are rejected.
BinaryVersion string
// OnBumped fires when the field was updated, carrying the previous
// value (empty string when no minimum was recorded) and the new value.
OnBumped func(previous, current string)
// OnUnchanged fires when the binary version equals the recorded
// minimum (or is lower than it — a no-op rather than a regression).
OnUnchanged func(current string)
}
BumpMinimumVersionCmd captures intent to raise .sdd/meta.json's minimum_version field to the running binary's version. Used by `sdd init --bump` to lock contributors out of older binaries after a breaking change has shipped — the inverse of the regular write path (which preserves whatever was set at graph creation).
The handler refuses dev builds: only released binaries are permitted to raise the floor, since otherwise local development could pin a graph to a non-released version no contributor outside the dev tree could match.
type FinishWIPCmd ¶
type FinishWIPCmd struct {
MarkerID string
Force bool // when true, force-delete an unmerged branch (discard flow)
// OnRemoved fires after the marker file is removed and the deletion
// is committed.
OnRemoved func(markerID string)
// OnBranchDeleted fires after a git branch is deleted (merged or
// force-deleted).
OnBranchDeleted func(branch string, forced bool)
// OnBranchPreserved fires when the marker referenced a branch but the
// branch was unmerged and Force was not set — branch is preserved,
// callback informs the CLI to print guidance.
OnBranchPreserved func(branch string)
}
FinishWIPCmd captures intent to remove a WIP marker (and optionally clean up its git branch).
func (*FinishWIPCmd) Validate ¶
func (c *FinishWIPCmd) Validate() error
Validate checks required fields.
type InitCmd ¶
type InitCmd struct {
// RepoRoot is the absolute path to the repository root where .sdd/
// will live.
RepoRoot string
// GraphDir is the graph directory path relative to RepoRoot. Empty
// defaults to model.DefaultGraphDir (".sdd/graph").
GraphDir string
// Participant is the canonical author name to record in
// .sdd/config.local.yaml. Empty means "do not change" — existing
// values in the local config are preserved (caller resolves the value
// interactively if a prompt is warranted). The handler writes the
// field into the mapping without disturbing other keys (e.g. llm:).
Participant string
// Language is the graph authoring language (locale code) to record in
// .sdd/config.yaml on fresh init. Empty means "use the default behavior"
// — FormatConfig keeps the commented hint, so the graph stays English.
// Only applied on fresh init; existing config.yaml is never rewritten.
Language string
// BinaryVersion is the running sdd binary's version. Stamped into each
// installed skill file's frontmatter and, on initial init only, used
// to derive the graph's minimum_version (unless it's a dev build).
BinaryVersion string
// Target selects which agent's skills to install. Empty defaults to
// model.DefaultAgentTarget (Claude).
Target model.AgentTarget
// Scope selects user-global vs project-local skill installation. Empty
// defaults to model.DefaultScope (User). When ScopeExplicit is false the
// handler treats Scope as a fallback and prefers any value already
// recorded under skill_scope in .sdd/config.yaml; when true the handler
// rejects a value that contradicts the recorded scope (AC 2).
Scope model.Scope
// ScopeExplicit reports whether the caller passed --scope on the
// command line versus letting it default. Used at the handler boundary
// to distinguish "user opted in to this scope" from "no choice has
// been made yet" — the contradiction check (AC 2) only applies when
// the operator typed a value.
ScopeExplicit bool
// UserHome is the absolute path to the user's home directory. Required
// when Scope = User.
UserHome string
// Force unconditionally overwrites user-modified skill files,
// bypassing PromptOverwrite entirely.
Force bool
// Bump asks the handler to raise .sdd/meta.json's minimum_version to
// the running binary's version after the regular init flow runs. Dev
// builds are rejected with the fixed message defined on
// BumpMinimumVersionCmd; equal versions are a no-op.
Bump bool
// OnMinimumVersionBumped fires when minimum_version was raised by a
// `sdd init --bump` invocation, carrying the previous value (empty
// when no floor was recorded) and the new value.
OnMinimumVersionBumped func(previous, current string)
// OnMinimumVersionUnchanged fires when --bump was passed but the
// binary version already matched the recorded minimum (no-op).
OnMinimumVersionUnchanged func(current string)
// PromptOverwrite is invoked for each skill file whose on-disk copy
// has been user-edited. Return true to overwrite, false to preserve.
// If nil (and Force is false), modified skill files are preserved
// without prompting.
PromptOverwrite func(absPath string) (bool, error)
// OnCreated fires after .sdd/, config.yaml, and the graph dir are
// freshly created on an empty tree.
OnCreated func(sddDir, absGraphDir string)
// OnMigrated fires after .sdd-tmp/ contents are migrated to .sdd/tmp/.
OnMigrated func(count int)
// OnGitignoreUpdated fires after .gitignore is updated with .sdd/tmp/.
OnGitignoreUpdated func(gitignorePath string)
// OnMetaWritten fires when .sdd/meta.json is created. Does not fire
// when an existing meta.json is preserved.
OnMetaWritten func(path string)
// OnMetaPreserved fires when .sdd/meta.json already existed and was
// left untouched.
OnMetaPreserved func(path string)
// OnParticipantWritten fires when the participant field is added to or
// updated in .sdd/config.local.yaml. Does not fire when the existing
// value already matches (idempotent re-init produces no callback).
OnParticipantWritten func(path, name string)
// OnSkillsInstalled fires after the skill install pass completes,
// carrying a per-category summary suitable for presenter output.
OnSkillsInstalled func(result SkillInstallResult)
}
InitCmd captures intent to initialize (or refresh) an SDD project. The handler is idempotent — running it on a fresh tree creates .sdd/, writes config.yaml and meta.json, and installs the embedded skill bundle; running it against a tree that's already set up refreshes what's drifted and leaves the rest alone.
type InstallSkillsCmd ¶ added in v0.2.0
type InstallSkillsCmd struct {
Target model.AgentTarget
Scope model.Scope
RepoRoot string
UserHome string
BinaryVersion string
// Force unconditionally overwrites user-modified files without
// consulting PromptOverwrite. Intended for non-interactive runs where
// the operator has already decided to discard local edits.
Force bool
// PromptOverwrite is invoked for each file whose on-disk copy has been
// user-edited. Return true to overwrite the file with the embedded
// version, false to leave it in place. If nil and Force is false,
// modified files are preserved without prompting (treated as a "no"
// response).
PromptOverwrite func(absPath string) (overwrite bool, err error)
// OnInstalled fires once after all files have been processed, carrying
// a per-category summary for presenter consumption.
OnInstalled func(result SkillInstallResult)
}
InstallSkillsCmd captures intent to extract the embedded skill bundle onto a target agent's skill directory. Idempotent: files already matching the embedded bundle are skipped, pristine files are refreshed silently, and user-modified files are routed through PromptOverwrite.
type LazyFillIndexCmd ¶ added in v0.4.0
type LazyFillIndexCmd struct {
// OnBatchStart mirrors BuildIndexCmd's callback — fired before each
// embedding round-trip with the batch's entry IDs, for progress reporting.
OnBatchStart func(entryIDs []string)
// OnEntryIndexed mirrors BuildIndexCmd's callback.
OnEntryIndexed func(entryID string, chunkCount int)
// OnComplete is called once after lazy-fill finishes, with the count
// of entries that were re-embedded.
OnComplete func(indexed int)
}
LazyFillIndexCmd reconciles the index against entries currently on disk: any entry not yet indexed (or whose stored hash/fingerprint differs from the configured embedder) is re-embedded and upserted. Used by sdd search before the query so cold-start cost on a fresh clone or branch switch is paid lazily rather than requiring an explicit warm-up.
type LintFixCmd ¶
type LintFixCmd struct {
// OnFixed is invoked for each entry that was fixed, with the entry ID
// and a list of human-readable fix descriptions.
OnFixed func(id string, fixes []string)
}
LintFixCmd captures intent to apply mechanical fixes to graph entries. The handler loads the graph, applies all available fixers, writes patched files, and commits. OnFixed is invoked per entry with the fixes applied.
type NewEntryCmd ¶
type NewEntryCmd struct {
Type model.EntryType
Layer model.Layer
Kind model.Kind // empty is replaced by the type's default in BuildEntry
Description string
Refs []model.Ref
Supersedes []string
Closes []string
Participants []string
Confidence string
Attachments []Attachment
// Canonical and Aliases are only meaningful for kind: actor signals —
// copied into the written frontmatter so downstream readers can
// resolve participant identity. Ignored on non-actor entries; the
// model-layer validator flags a missing canonical on an actor.
Canonical string
Aliases []string
// Actor is only meaningful for kind: role decisions — names the
// canonical of the actor-identity chain the role binds to. Ignored
// on non-role entries.
Actor string
// TopicLabels carries inline topic labels (CSV form on the CLI; flat
// list here). Valid on any non-annotation entry. Stored verbatim;
// parsing into TopicPath happens during BuildEntry so the command
// surface stays simple.
TopicLabels []string
// AnnotationTopics carries the topic assignments for a kind: annotation
// entry. Each --topic flag at the CLI parses into one entry here. The
// item shape supports plain label (members empty → applies to all refs)
// or label + member sub-selection.
AnnotationTopics []model.AnnotationTopic
// FocusActors is the focus-level default actor list (canonical-only).
// Only meaningful on kind: focus decisions.
FocusActors []string
// FocusWhen is the focus-level default temporal scope. Per-involvement
// when overrides this. Only meaningful on kind: focus decisions.
FocusWhen *model.FocusWhen
// Involvement is the list of involvement triples for a kind: focus
// decision. Each --involvement flag parses into one entry here.
Involvement []model.Involvement
SkipPreflight bool
DryRun bool
PreflightTimeout time.Duration
PreflightModel string
// OnNewEntry is invoked with the new entry's ID and the LLM-generated
// summary on successful creation. Summary is empty when the LLM call
// failed or was skipped (dry-run path returns before invoking). Not
// invoked on dry-run or any failure path. For richer data (path,
// content), the caller issues a query against the appropriate finder.
OnNewEntry func(id, summary string)
// OnPreflight is invoked with the pre-flight result whenever the
// validator ran to completion — including when its findings block the
// entry. Not invoked on skip, hard validator errors, or paths that never
// reach pre-flight. Transport layers that need findings structurally
// (MCP) hook this; the CLI relies on the handler's stderr rendering.
OnPreflight func(result *query.PreflightResult)
}
NewEntryCmd captures intent to create a new graph entry. The handler is responsible for graph loading, validation, pre-flight, stdin persistence on rejection/dry-run, writing the entry file, copying attachments, and committing. On success the handler invokes OnNewEntry with the new entry's ID; the caller queries a finder for any richer data.
func (*NewEntryCmd) BuildEntry ¶
func (c *NewEntryCmd) BuildEntry(id string) (*model.Entry, error)
BuildEntry constructs a model.Entry from the command fields, applying defaults (Kind → directive for decisions) and resolving attachment paths and content links. The caller provides the generated entry ID.
func (*NewEntryCmd) StdinAttachment ¶
func (c *NewEntryCmd) StdinAttachment() *Attachment
StdinAttachment returns the single stdin attachment, or nil if none is present. (parseAttachFlags enforces at most one stdin attachment at the CLI layer.)
func (*NewEntryCmd) Validate ¶
func (c *NewEntryCmd) Validate() error
Validate checks that required fields are populated and internally consistent. Richer validation (refs exist in graph, type constraints on closes, etc.) is the handler's job against a loaded graph.
type RewriteEntryCmd ¶ added in v0.2.0
type RewriteEntryCmd struct {
EntryID string // full ID (or short form resolved before handler)
NewType model.EntryType // target type
NewKind model.Kind // target kind
Message string // optional commit message override
DryRun bool // when true: validate and report; do not write or commit
NoCommit bool // when true: write changes to disk but skip git commit
// OnRewritten fires after a successful rewrite (real or dry-run) with the
// old id, new id, and the list of inbound entry ids whose references were
// updated. For richer data, the caller issues a follow-up query.
OnRewritten func(oldID, newID string, inboundUpdated []string)
}
RewriteEntryCmd captures intent to atomically change an entry's type and/or kind. The handler renames the file (when type changes), rewrites frontmatter, updates every inbound refs/closes/supersedes reference across the graph, and commits the result as one unit. Bypasses pre-flight — this is a mechanical fix per d-cpt-e1i, not a capture that warrants LLM validation.
func (*RewriteEntryCmd) Validate ¶ added in v0.2.0
func (c *RewriteEntryCmd) Validate() error
Validate checks command-level invariants. Graph-resolved checks (entry exists, new ID doesn't collide with an existing entry) are the handler's job.
type SkillInstallResult ¶ added in v0.2.0
type SkillInstallResult struct {
// InstallDir is the absolute directory the bundle was written into.
// Surfaced so presenters and callers don't have to recompute the path
// from scope + repoRoot + userHome — the handler already knows it.
InstallDir string
Installed []string // entry was missing, now written
Refreshed []string // entry was pristine (user hadn't edited), now rewritten with fresh stamps
Overwritten []string // entry was modified, prompt returned true
SkippedModified []string // entry was modified, prompt returned false (or no prompt supplied)
Current []string // entry already matched the embedded bundle — no write
}
SkillInstallResult categorises each bundle entry's outcome after the handler runs. AbsPath values are returned (not relative) so presenters can surface them directly to the user.
type StartWIPCmd ¶
type StartWIPCmd struct {
EntryID string
Description string
Participant string
Exclusive bool
Branch bool // when true, derive a branch name and create+checkout
// OnStarted fires after the marker file is written and committed.
// Callbacks receive only identifiers — the caller queries finders for
// richer data if needed (consistent with d-cpt-l3s).
OnStarted func(markerID, markerPath string)
// OnBranchCreated fires after the git branch is created and checked
// out. Only invoked when Branch is true.
OnBranchCreated func(branchName string)
// OnExclusiveCollision fires when an existing exclusive marker is
// found for the same entry. The handler doesn't block creation; the
// callback lets the CLI print a warning.
OnExclusiveCollision func(existing *model.WIPMarker)
}
StartWIPCmd captures intent to create a WIP marker for a graph entry, optionally creating and checking out a git branch in the same step.
func (*StartWIPCmd) Validate ¶
func (c *StartWIPCmd) Validate() error
Validate checks required fields.
type SummarizeCmd ¶
type SummarizeCmd struct {
// EntryIDs lists specific entries to summarize. Empty means --all.
EntryIDs []string
// Force regenerates summaries even if the hash matches.
Force bool
// Model is the LLM model to use for summary generation.
Model string
// Timeout per entry for the LLM call.
Timeout time.Duration
// Concurrency bounds the worker pool for batch summarize. Zero falls
// back to model.DefaultLLMConcurrency.
Concurrency int
// ExplicitText, when non-nil, is written as the entry's summary directly
// without invoking the LLM. Single entry only — handler rejects when set
// alongside multiple EntryIDs or empty EntryIDs (--all). The summary hash
// is recomputed from the current prompt input so subsequent automatic
// regenerations skip-by-hash unless Force is set.
ExplicitText *string
// OnSummarized is called for each entry that gets a new summary.
OnSummarized func(id, summary string)
// OnSkipped is called for each entry whose hash matched (skipped).
OnSkipped func(id string)
}
SummarizeCmd captures intent to generate or regenerate entry summaries.
type SyncPullCmd ¶ added in v0.8.0
type SyncPullCmd struct {
// OnPulled is invoked after a successful merge pull with git's combined
// stdout/stderr — the text the user wants to see verbatim.
OnPulled func(output string)
}
SyncPullCmd captures intent to pull the shared graph from the upstream branch using a merge (never a rebase), so background sync never rewrites the shared history. The handler refuses on a dirty working tree and defers to the user. On a successful pull it invokes OnPulled with git's own output (the "Already up to date." line or the merge summary).
type WriteSchemaMetaCmd ¶ added in v0.2.0
type WriteSchemaMetaCmd struct {
// SDDDir is the absolute path to the .sdd/ directory.
SDDDir string
// SchemaVersion is the graph schema version to stamp into a newly
// written meta.json. Ignored when the file already exists.
SchemaVersion int
// MinimumVersion is the oldest sdd binary permitted to write to this
// graph. Written only when the meta file does not yet exist; passing
// nil skips writing the field (dev builds use this to avoid pinning a
// floor during local development).
MinimumVersion *string
// OnWritten fires after a fresh meta.json has been written with the
// absolute path as argument. Does not fire for no-op runs against an
// existing file.
OnWritten func(path string)
// OnPreserved fires when meta.json already existed and was left
// untouched, carrying the absolute path.
OnPreserved func(path string)
}
WriteSchemaMetaCmd captures intent to write .sdd/meta.json on initial graph setup. The handler is a no-op when the file already exists — both graph_schema_version (bumped by migrations only) and minimum_version (set once at creation, preserved thereafter) are write-once semantics.