seam-code 0.3.0__py3-none-any.whl
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.
- seam/__init__.py +3 -0
- seam/_data/schema.sql +225 -0
- seam/_web/assets/index-BL_tqprR.js +216 -0
- seam/_web/assets/index-GTKUhVyD.css +1 -0
- seam/_web/index.html +13 -0
- seam/analysis/__init__.py +14 -0
- seam/analysis/affected.py +254 -0
- seam/analysis/builtins.py +966 -0
- seam/analysis/byte_budget.py +217 -0
- seam/analysis/changes.py +709 -0
- seam/analysis/cluster_naming.py +260 -0
- seam/analysis/clustering.py +216 -0
- seam/analysis/confidence.py +699 -0
- seam/analysis/embeddings.py +195 -0
- seam/analysis/flows.py +708 -0
- seam/analysis/impact.py +444 -0
- seam/analysis/imports.py +994 -0
- seam/analysis/imports_ext.py +780 -0
- seam/analysis/imports_resolve.py +176 -0
- seam/analysis/processes.py +453 -0
- seam/analysis/relevance.py +155 -0
- seam/analysis/rwr.py +129 -0
- seam/analysis/staleness.py +328 -0
- seam/analysis/steer.py +282 -0
- seam/analysis/synthesis.py +253 -0
- seam/analysis/synthesis_channels.py +433 -0
- seam/analysis/testpaths.py +103 -0
- seam/analysis/traversal.py +470 -0
- seam/cli/__init__.py +0 -0
- seam/cli/install.py +232 -0
- seam/cli/main.py +2602 -0
- seam/cli/output.py +137 -0
- seam/cli/read.py +244 -0
- seam/cli/serve.py +145 -0
- seam/config.py +551 -0
- seam/indexer/__init__.py +0 -0
- seam/indexer/cluster_index.py +425 -0
- seam/indexer/db.py +496 -0
- seam/indexer/embedding_index.py +183 -0
- seam/indexer/field_access.py +536 -0
- seam/indexer/field_access_c_cpp.py +643 -0
- seam/indexer/field_access_ext.py +708 -0
- seam/indexer/field_access_ext2.py +408 -0
- seam/indexer/field_access_go_rust.py +737 -0
- seam/indexer/field_access_php_swift.py +888 -0
- seam/indexer/field_access_ts.py +626 -0
- seam/indexer/graph.py +321 -0
- seam/indexer/graph_c.py +562 -0
- seam/indexer/graph_c_cpp.py +39 -0
- seam/indexer/graph_common.py +644 -0
- seam/indexer/graph_cpp.py +615 -0
- seam/indexer/graph_csharp.py +651 -0
- seam/indexer/graph_go.py +723 -0
- seam/indexer/graph_go_rust.py +39 -0
- seam/indexer/graph_java.py +689 -0
- seam/indexer/graph_java_csharp.py +38 -0
- seam/indexer/graph_php.py +914 -0
- seam/indexer/graph_python.py +628 -0
- seam/indexer/graph_ruby.py +748 -0
- seam/indexer/graph_rust.py +653 -0
- seam/indexer/graph_scope_infer.py +902 -0
- seam/indexer/graph_scope_infer_ext.py +723 -0
- seam/indexer/graph_scope_infer_ext2.py +992 -0
- seam/indexer/graph_swift.py +1014 -0
- seam/indexer/graph_swift_infer.py +515 -0
- seam/indexer/graph_typescript.py +663 -0
- seam/indexer/migrations.py +816 -0
- seam/indexer/parser.py +204 -0
- seam/indexer/pipeline.py +197 -0
- seam/indexer/signatures.py +634 -0
- seam/indexer/signatures_ext.py +780 -0
- seam/indexer/sync.py +287 -0
- seam/indexer/synthesis_index.py +291 -0
- seam/indexer/tokenize.py +79 -0
- seam/installer/__init__.py +67 -0
- seam/installer/claude.py +97 -0
- seam/installer/codex.py +94 -0
- seam/installer/core.py +127 -0
- seam/installer/cursor.py +61 -0
- seam/installer/guide.py +110 -0
- seam/installer/jsonfile.py +85 -0
- seam/installer/markdownfile.py +146 -0
- seam/installer/tomlfile.py +72 -0
- seam/query/__init__.py +0 -0
- seam/query/clusters.py +206 -0
- seam/query/comments.py +217 -0
- seam/query/context.py +293 -0
- seam/query/engine.py +940 -0
- seam/query/fts.py +328 -0
- seam/query/names.py +470 -0
- seam/query/pack.py +433 -0
- seam/query/semantic.py +339 -0
- seam/query/structure.py +727 -0
- seam/server/__init__.py +0 -0
- seam/server/graph_api.py +437 -0
- seam/server/handler_common.py +323 -0
- seam/server/impact_handler.py +615 -0
- seam/server/mcp.py +556 -0
- seam/server/tools.py +697 -0
- seam/server/trace_handler.py +184 -0
- seam/server/web.py +922 -0
- seam/watcher/__init__.py +0 -0
- seam/watcher/__main__.py +56 -0
- seam/watcher/daemon.py +237 -0
- seam_code-0.3.0.dist-info/METADATA +318 -0
- seam_code-0.3.0.dist-info/RECORD +109 -0
- seam_code-0.3.0.dist-info/WHEEL +4 -0
- seam_code-0.3.0.dist-info/entry_points.txt +2 -0
- seam_code-0.3.0.dist-info/licenses/LICENSE +21 -0
seam/__init__.py
ADDED
seam/_data/schema.sql
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
-- Seam SQLite Schema (v12 — edge-synthesis post-pass: edges.synthesized_by provenance)
|
|
2
|
+
-- Run via db.py:init_db() — idempotent (CREATE TABLE IF NOT EXISTS).
|
|
3
|
+
-- FTS5 is required; init_db() verifies availability before proceeding.
|
|
4
|
+
-- Schema v2 adds: edges.confidence (DEFAULT 'INFERRED').
|
|
5
|
+
-- Schema v3 adds: comments table (WHY/HACK/NOTE/TODO/FIXME semantic comments).
|
|
6
|
+
-- Schema v4 adds: clusters table + symbols.cluster_id (graph community detection).
|
|
7
|
+
-- Schema v5 adds: symbols.signature, decorators, is_exported, visibility, qualified_name;
|
|
8
|
+
-- FTS5 rebuilt to index (name, docstring, signature).
|
|
9
|
+
-- Schema v6 adds: import_mappings table (Phase 5 import resolution).
|
|
10
|
+
-- Schema v7 adds: embeddings table (semantic search via local fastembed vectors).
|
|
11
|
+
-- Schema v8 adds: clusters.cohesion (P2 internal-edge ratio; small search bonus).
|
|
12
|
+
-- Schema v9 adds: symbols.entry_score (P6b framework entry-point ranking multiplier).
|
|
13
|
+
-- Schema v10 adds: edges.receiver (Tier B B1: raw receiver text for attribute calls;
|
|
14
|
+
-- NULL for import/bare-call edges and pre-v10 rows).
|
|
15
|
+
-- Schema v11 adds: symbols.search_text (Tier D #12: camelCase/snake_case-split tokens;
|
|
16
|
+
-- FTS5 rebuilt to index (name, docstring, signature, search_text)).
|
|
17
|
+
-- Schema v12 adds: edges.synthesized_by (edge-synthesis post-pass provenance;
|
|
18
|
+
-- NULL = parser-extracted; channel name = synthesized by post-pass).
|
|
19
|
+
-- Migration from v1: db.py:_run_migration_v1_to_v2() (guarded ALTER TABLE).
|
|
20
|
+
-- Migration from v2: db.py:_run_migration_v2_to_v3() (guards schema_version bump).
|
|
21
|
+
-- Migration from v3: db.py:_run_migration_v3_to_v4() (adds clusters table + cluster_id).
|
|
22
|
+
-- Migration from v4: db.py:_run_migration_v4_to_v5() (adds 5 enrichment cols + FTS rebuild).
|
|
23
|
+
-- Migration from v5: db.py:_run_migration_v5_to_v6() (adds import_mappings table).
|
|
24
|
+
-- Migration from v6: db.py:_run_migration_v6_to_v7() (adds embeddings table).
|
|
25
|
+
-- Migration from v7: db.py:_run_migration_v7_to_v8() (adds clusters.cohesion column).
|
|
26
|
+
-- Migration from v8: db.py:_run_migration_v8_to_v9() (adds symbols.entry_score column).
|
|
27
|
+
-- Migration from v9: db.py:_run_migration_v9_to_v10() (adds edges.receiver column).
|
|
28
|
+
-- Migration from v10: db.py:_run_migration_v10_to_v11() (adds symbols.search_text + FTS column).
|
|
29
|
+
-- Migration from v11: db.py:_run_migration_v11_to_v12() (adds edges.synthesized_by column).
|
|
30
|
+
|
|
31
|
+
PRAGMA journal_mode = WAL; -- Write-ahead logging for concurrent reads
|
|
32
|
+
PRAGMA foreign_keys = ON;
|
|
33
|
+
PRAGMA synchronous = NORMAL; -- Faster than FULL; safe with WAL
|
|
34
|
+
|
|
35
|
+
-- ── Files ────────────────────────────────────────────────────────────────────
|
|
36
|
+
-- One row per indexed source file. Used to detect stale files on re-index.
|
|
37
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
38
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
+
path TEXT NOT NULL UNIQUE, -- Absolute path (resolved at index time)
|
|
40
|
+
language TEXT NOT NULL, -- 'python' | 'typescript' | 'javascript'
|
|
41
|
+
file_hash TEXT NOT NULL, -- SHA1 of file content (for change detection)
|
|
42
|
+
mtime REAL NOT NULL, -- os.stat().st_mtime at index time
|
|
43
|
+
indexed_at REAL NOT NULL -- time.time() when this file was indexed
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);
|
|
47
|
+
|
|
48
|
+
-- ── Symbols ──────────────────────────────────────────────────────────────────
|
|
49
|
+
-- One row per extracted symbol (function, class, method, interface, etc.)
|
|
50
|
+
-- Phase 4 adds: signature, decorators, is_exported, visibility, qualified_name.
|
|
51
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
52
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
53
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
54
|
+
name TEXT NOT NULL, -- Fully-qualified when possible (Class.method)
|
|
55
|
+
kind TEXT NOT NULL, -- 'function' | 'class' | 'method' | 'interface' | 'type' | 'field' [A3]
|
|
56
|
+
start_line INTEGER NOT NULL,
|
|
57
|
+
end_line INTEGER NOT NULL,
|
|
58
|
+
docstring TEXT, -- First docstring/JSDoc block if present; NULL otherwise
|
|
59
|
+
cluster_id INTEGER, -- FK to clusters.id; NULL until clustering post-pass runs
|
|
60
|
+
-- Phase 4 node-enrichment fields (all nullable; NULL when not extracted or pre-v5 rows)
|
|
61
|
+
signature TEXT, -- Declaration header, one line, truncated to SEAM_MAX_SIGNATURE_LEN
|
|
62
|
+
decorators TEXT, -- JSON-encoded list of decorator strings; '[]' for empty
|
|
63
|
+
is_exported INTEGER, -- 1=exported/public, 0=unexported/private, NULL=unknown
|
|
64
|
+
visibility TEXT, -- 'public' | 'private' | 'protected' | 'crate' | NULL
|
|
65
|
+
qualified_name TEXT, -- 'ClassName.method' or plain name; NULL when unknown
|
|
66
|
+
-- P6b (v9): framework entry-point ranking multiplier (>=1.0). Computed at index
|
|
67
|
+
-- time from the file path pattern + decorator text (processes.compute_entry_score).
|
|
68
|
+
-- NULL on pre-v9 rows until re-index; list_entry_points treats NULL as baseline 1.0.
|
|
69
|
+
entry_score REAL,
|
|
70
|
+
-- Tier D #12 (v11): camelCase/snake_case-split tokens of name + qualified_name, space-
|
|
71
|
+
-- joined and deduped (tokenize.build_search_text). Indexed as a 4th symbols_fts column so
|
|
72
|
+
-- "push to talk monitor" matches GlobalPushToTalkShortcutMonitor. NULL on pre-v11 rows and
|
|
73
|
+
-- when SEAM_TOKENIZE_IDENTIFIERS=off → no split-token recall until re-index.
|
|
74
|
+
search_text TEXT
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_file_id ON symbols(file_id);
|
|
79
|
+
|
|
80
|
+
-- ── Edges ────────────────────────────────────────────────────────────────────
|
|
81
|
+
-- Directed relationships between symbols.
|
|
82
|
+
-- source_name / target_name store the string name (not ID) so edges survive
|
|
83
|
+
-- re-indexing of either endpoint independently.
|
|
84
|
+
-- confidence: EXTRACTED (resolved to 1 symbol) | INFERRED (heuristic) | AMBIGUOUS (name collision)
|
|
85
|
+
-- receiver: raw receiver expression text for attribute calls (e.g., 'self', 'obj').
|
|
86
|
+
-- NULL for import edges, bare-identifier call edges, and pre-v10 rows.
|
|
87
|
+
-- Captured at extraction time to enable later receiver-type inference (Tier B).
|
|
88
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
89
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
90
|
+
source_name TEXT NOT NULL, -- Symbol name of the caller/importer
|
|
91
|
+
target_name TEXT NOT NULL, -- Symbol name of the callee/importee
|
|
92
|
+
kind TEXT NOT NULL, -- 'import' | 'call' | 'extends' | 'implements' | 'instantiates' | 'holds' | 'reads' | 'writes' [A3]
|
|
93
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
94
|
+
line INTEGER NOT NULL, -- Line where the relationship is expressed
|
|
95
|
+
confidence TEXT NOT NULL DEFAULT 'INFERRED', -- EXTRACTED | INFERRED | AMBIGUOUS (DEFAULT is INFERRED: conservative)
|
|
96
|
+
-- Tier B B1 (v10): receiver text from attribute call expressions (recv.method).
|
|
97
|
+
-- NULL for import edges, bare calls, and pre-v10 rows (null-contract: same as Phase 4/5 fields).
|
|
98
|
+
receiver TEXT,
|
|
99
|
+
-- v12 (edge-synthesis post-pass): channel that synthesized this edge.
|
|
100
|
+
-- NULL = statically extracted by a parser (the default for all extractor-emitted edges).
|
|
101
|
+
-- A channel name string = synthesized by the post-pass, e.g. 'interface-override'.
|
|
102
|
+
-- Provenance is DERIVED: synthesized_by IS NOT NULL ⟹ heuristic edge.
|
|
103
|
+
-- Synthesized edges appear only after the next full 'seam init' (explicit backfill,
|
|
104
|
+
-- same null-contract as prior enrichment columns). The synthesis pass stores these
|
|
105
|
+
-- under a permanent ':synthesis:' file row (not a real on-disk file).
|
|
106
|
+
synthesized_by TEXT
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_name);
|
|
110
|
+
CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_name);
|
|
111
|
+
|
|
112
|
+
-- ── FTS5 (Full-Text Search) ───────────────────────────────────────────────────
|
|
113
|
+
-- Virtual table for BM25-ranked search across symbol names + docstrings + signatures
|
|
114
|
+
-- + search_text (Tier D #12 split tokens). Read-path bm25() weights search_text lowest.
|
|
115
|
+
-- Phase 5 change: added 'signature' column so type-shaped queries match on params/returns.
|
|
116
|
+
-- Kept in sync with 'symbols' via triggers below.
|
|
117
|
+
-- Tier D #12 (v11): search_text column added — split identifier tokens for camelCase recall.
|
|
118
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(
|
|
119
|
+
name,
|
|
120
|
+
docstring,
|
|
121
|
+
signature,
|
|
122
|
+
search_text,
|
|
123
|
+
content='symbols',
|
|
124
|
+
content_rowid='id'
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
-- Triggers: keep FTS5 in sync with symbols (avoids manual sync in application code)
|
|
128
|
+
CREATE TRIGGER IF NOT EXISTS symbols_ai AFTER INSERT ON symbols BEGIN
|
|
129
|
+
INSERT INTO symbols_fts(rowid, name, docstring, signature, search_text)
|
|
130
|
+
VALUES (new.id, new.name, new.docstring, new.signature, new.search_text);
|
|
131
|
+
END;
|
|
132
|
+
|
|
133
|
+
CREATE TRIGGER IF NOT EXISTS symbols_ad AFTER DELETE ON symbols BEGIN
|
|
134
|
+
INSERT INTO symbols_fts(symbols_fts, rowid, name, docstring, signature, search_text)
|
|
135
|
+
VALUES ('delete', old.id, old.name, old.docstring, old.signature, old.search_text);
|
|
136
|
+
END;
|
|
137
|
+
|
|
138
|
+
CREATE TRIGGER IF NOT EXISTS symbols_au AFTER UPDATE ON symbols BEGIN
|
|
139
|
+
INSERT INTO symbols_fts(symbols_fts, rowid, name, docstring, signature, search_text)
|
|
140
|
+
VALUES ('delete', old.id, old.name, old.docstring, old.signature, old.search_text);
|
|
141
|
+
INSERT INTO symbols_fts(rowid, name, docstring, signature, search_text)
|
|
142
|
+
VALUES (new.id, new.name, new.docstring, new.signature, new.search_text);
|
|
143
|
+
END;
|
|
144
|
+
|
|
145
|
+
-- ── Comments ─────────────────────────────────────────────────────────────────
|
|
146
|
+
-- Semantic comments extracted during indexing: WHY/HACK/NOTE/TODO/FIXME markers.
|
|
147
|
+
-- Cascade-deleted when the parent file is removed or re-indexed.
|
|
148
|
+
-- No FTS: lookup is by file_id + line range (proximity), not full-text.
|
|
149
|
+
CREATE TABLE IF NOT EXISTS comments (
|
|
150
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
151
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
152
|
+
line INTEGER NOT NULL, -- 1-based line number in the source file
|
|
153
|
+
marker TEXT NOT NULL, -- Normalized UPPERCASE: WHY|HACK|NOTE|TODO|FIXME
|
|
154
|
+
text TEXT NOT NULL -- Comment body after the marker (and optional colon), stripped
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
-- Index to speed up file-scoped lookups (the dominant query pattern).
|
|
158
|
+
CREATE INDEX IF NOT EXISTS idx_comments_file_id ON comments(file_id);
|
|
159
|
+
|
|
160
|
+
-- ── Clusters ─────────────────────────────────────────────────────────────────
|
|
161
|
+
-- One row per community detected during `seam init` clustering post-pass.
|
|
162
|
+
-- Populated after the full indexing loop, not per-file. Cleared and repopulated
|
|
163
|
+
-- on each full `seam init`. Watcher does NOT recompute clusters.
|
|
164
|
+
CREATE TABLE IF NOT EXISTS clusters (
|
|
165
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
166
|
+
label TEXT NOT NULL, -- Human-readable label (deterministic or LLM-generated)
|
|
167
|
+
size INTEGER NOT NULL, -- Number of member symbols
|
|
168
|
+
naming_source TEXT NOT NULL, -- 'deterministic' | 'llm'
|
|
169
|
+
-- P2 (v8): internal-edge ratio in [0,1]. NULL on pre-v8 rows until re-index.
|
|
170
|
+
-- Higher = tighter, more self-contained community. Small additive search bonus.
|
|
171
|
+
cohesion REAL
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_clusters_id ON clusters(id);
|
|
175
|
+
|
|
176
|
+
-- ── Import Mappings ──────────────────────────────────────────────────────────
|
|
177
|
+
-- One row per import binding extracted from a source file during indexing.
|
|
178
|
+
-- Populated by pipeline.py (index_one_file) alongside symbols/edges.
|
|
179
|
+
-- Refreshed per-file by the watcher (delete-then-insert, same as upsert_file).
|
|
180
|
+
-- NOT backfilled by migration — only a full `seam init` populates these rows.
|
|
181
|
+
-- Schema v6 addition (Phase 5: import resolution confidence promotion).
|
|
182
|
+
CREATE TABLE IF NOT EXISTS import_mappings (
|
|
183
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
184
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
185
|
+
local_name TEXT NOT NULL, -- name used in the referencing file (alias or original)
|
|
186
|
+
exported_name TEXT NOT NULL, -- original name in the source module
|
|
187
|
+
source_module TEXT NOT NULL, -- import source as written (e.g. 'app.parser', './utils')
|
|
188
|
+
is_default INTEGER NOT NULL DEFAULT 0, -- 1 for default imports (import X from ...)
|
|
189
|
+
is_namespace INTEGER NOT NULL DEFAULT 0, -- 1 for namespace imports (import * as ns)
|
|
190
|
+
is_wildcard INTEGER NOT NULL DEFAULT 0, -- 1 for wildcard imports (from x import *)
|
|
191
|
+
line INTEGER NOT NULL -- 1-based line number of the import statement
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
-- Index for fast lookup by file (primary access pattern: load all mappings for a file)
|
|
195
|
+
CREATE INDEX IF NOT EXISTS idx_import_mappings_file_id ON import_mappings(file_id);
|
|
196
|
+
-- Index for fast lookup by local name (used in import-promotion step A)
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_import_mappings_local_name ON import_mappings(local_name);
|
|
198
|
+
|
|
199
|
+
-- ── Embeddings ───────────────────────────────────────────────────────────────
|
|
200
|
+
-- One row per symbol that has been embedded with a local fastembed model.
|
|
201
|
+
-- Populated ONLY by `seam init --semantic` (NOT by the base `seam init`).
|
|
202
|
+
-- Not backfilled by migration — embeddings stay absent until a --semantic run.
|
|
203
|
+
-- When absent (or model mismatch), seam_search/seam_query degrade to FTS5-only.
|
|
204
|
+
-- Schema v7 addition (semantic search foundation).
|
|
205
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
206
|
+
symbol_id INTEGER PRIMARY KEY REFERENCES symbols(id) ON DELETE CASCADE,
|
|
207
|
+
model TEXT NOT NULL, -- Model name used to produce this vector (e.g. 'BAAI/bge-small-en-v1.5')
|
|
208
|
+
dim INTEGER NOT NULL, -- Vector dimensionality (e.g. 384 for bge-small)
|
|
209
|
+
vector BLOB NOT NULL -- float32 bytes: numpy.array(..., dtype=np.float32).tobytes()
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
-- ── Metadata ─────────────────────────────────────────────────────────────────
|
|
213
|
+
-- Key-value store for index metadata (version, created_at, etc.)
|
|
214
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
215
|
+
key TEXT PRIMARY KEY,
|
|
216
|
+
value TEXT NOT NULL
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
-- NOTE: INSERT OR IGNORE does not update existing rows. Fresh DBs are seeded at
|
|
220
|
+
-- the CURRENT schema version ('12') so a brand-new `seam init` is born current and
|
|
221
|
+
-- does NOT trigger any migration advisory.
|
|
222
|
+
-- Existing older DBs keep their stored version; db.py migrations bump them in place.
|
|
223
|
+
INSERT OR IGNORE INTO metadata(key, value) VALUES
|
|
224
|
+
('schema_version', '12'),
|
|
225
|
+
('seam_version', '0.2.0');
|