skillwiki 0.10.10 → 0.10.12
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/{chunk-U34B2XQJ.js → chunk-NMUYMNNB.js} +2 -0
- package/dist/{chunk-ZFEZZQFM.js → chunk-YYWGP7DN.js} +26 -10
- package/dist/cli.js +441 -154
- package/dist/{index-projection-VNJ3TLEK.js → index-projection-MHXG7QO2.js} +5 -1
- package/dist/skillwiki-mcp.js +2 -2
- package/dist/vault-sync/scripts/lib/delete-intent.sh +27 -0
- package/dist/vault-sync/scripts/wiki-snapshot.sh +246 -43
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,10 +12,12 @@ import {
|
|
|
12
12
|
assessSourceIdentity,
|
|
13
13
|
buildDegradedReasons,
|
|
14
14
|
buildRemoteObjectPath,
|
|
15
|
+
canonicalEventJson,
|
|
15
16
|
clearLastOp,
|
|
16
17
|
configPath,
|
|
17
18
|
evaluateDirtyVolumeGate,
|
|
18
19
|
evaluateSatelliteRunHealth,
|
|
20
|
+
eventPathFor,
|
|
19
21
|
extractCitationMarkers,
|
|
20
22
|
extractTaxonomy,
|
|
21
23
|
findPlugin,
|
|
@@ -78,8 +80,9 @@ import {
|
|
|
78
80
|
scanConflictMarkerBlocksInText,
|
|
79
81
|
taxonomyCommentForPage,
|
|
80
82
|
upsertIndexEntry,
|
|
83
|
+
validateLogEvent,
|
|
81
84
|
writeLogEvent
|
|
82
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-YYWGP7DN.js";
|
|
83
86
|
import {
|
|
84
87
|
normalizeDistTag,
|
|
85
88
|
readCache,
|
|
@@ -88,6 +91,8 @@ import {
|
|
|
88
91
|
writeCache
|
|
89
92
|
} from "./chunk-7I2TPIV5.js";
|
|
90
93
|
import {
|
|
94
|
+
UNMANAGED_END,
|
|
95
|
+
UNMANAGED_START,
|
|
91
96
|
assertTargetInsideVault,
|
|
92
97
|
atomicWriteText,
|
|
93
98
|
extractFrontmatter,
|
|
@@ -99,7 +104,7 @@ import {
|
|
|
99
104
|
scanVault,
|
|
100
105
|
splitFrontmatter,
|
|
101
106
|
writeRootIndexProjection
|
|
102
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-NMUYMNNB.js";
|
|
103
108
|
import {
|
|
104
109
|
acquireManagedWriteLock,
|
|
105
110
|
releaseManagedWriteLock,
|
|
@@ -138,7 +143,7 @@ import {
|
|
|
138
143
|
} from "./chunk-C5OLZRRM.js";
|
|
139
144
|
|
|
140
145
|
// src/cli.ts
|
|
141
|
-
import { join as
|
|
146
|
+
import { join as join36 } from "path";
|
|
142
147
|
import { Command } from "commander";
|
|
143
148
|
|
|
144
149
|
// src/utils/output.ts
|
|
@@ -1683,10 +1688,265 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
|
|
|
1683
1688
|
};
|
|
1684
1689
|
}
|
|
1685
1690
|
|
|
1691
|
+
// src/commands/projections-repair-legacy.ts
|
|
1692
|
+
import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
|
|
1693
|
+
import { join as join13 } from "path";
|
|
1694
|
+
var OPERATION_ID_RE = /^[0-9a-f]{64}$/;
|
|
1695
|
+
var LEGACY_KEYS = ["created", "kind", "note", "operation_id", "target"];
|
|
1696
|
+
var EVENT_KEYS = [
|
|
1697
|
+
"actor",
|
|
1698
|
+
"host_id",
|
|
1699
|
+
"kind",
|
|
1700
|
+
"metadata",
|
|
1701
|
+
"note",
|
|
1702
|
+
"occurred_at",
|
|
1703
|
+
"operation_id",
|
|
1704
|
+
"schema",
|
|
1705
|
+
"target"
|
|
1706
|
+
];
|
|
1707
|
+
var defaultDeps2 = {
|
|
1708
|
+
writeText: (path, text) => atomicWriteText(path, text)
|
|
1709
|
+
};
|
|
1710
|
+
function isPlainObject(value) {
|
|
1711
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1712
|
+
}
|
|
1713
|
+
function hasExactKeys(value, keys) {
|
|
1714
|
+
const actual = Object.keys(value).sort();
|
|
1715
|
+
return actual.length === keys.length && actual.every((key, index) => key === keys[index]);
|
|
1716
|
+
}
|
|
1717
|
+
function count(text, token) {
|
|
1718
|
+
return text.split(token).length - 1;
|
|
1719
|
+
}
|
|
1720
|
+
function planIndexRepair(current) {
|
|
1721
|
+
if (count(current, UNMANAGED_START) !== 1 || count(current, UNMANAGED_END) !== 1) {
|
|
1722
|
+
return err("SCHEME_REJECTED", {
|
|
1723
|
+
path: "index.md",
|
|
1724
|
+
message: "index.md must contain exactly one complete unmanaged marker pair"
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
const start = current.indexOf(UNMANAGED_START);
|
|
1728
|
+
const end = current.indexOf(UNMANAGED_END);
|
|
1729
|
+
if (start < end) return ok({ current, repaired: current, needed: false });
|
|
1730
|
+
const between = current.slice(end + UNMANAGED_END.length, start);
|
|
1731
|
+
if (!/^\s*$/.test(between)) {
|
|
1732
|
+
return err("SCHEME_REJECTED", {
|
|
1733
|
+
path: "index.md",
|
|
1734
|
+
message: "reversed unmanaged markers must be adjacent except for whitespace"
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
const repaired = current.slice(0, end) + UNMANAGED_START + between + UNMANAGED_END + current.slice(start + UNMANAGED_START.length);
|
|
1738
|
+
return ok({ current, repaired, needed: true });
|
|
1739
|
+
}
|
|
1740
|
+
function normalizeLegacyTimestamp(value) {
|
|
1741
|
+
if (typeof value !== "string") {
|
|
1742
|
+
return err("SCHEME_REJECTED", { message: "legacy event created must be a UTC timestamp" });
|
|
1743
|
+
}
|
|
1744
|
+
const match = value.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{3}))?Z$/);
|
|
1745
|
+
if (!match) {
|
|
1746
|
+
return err("SCHEME_REJECTED", { message: "legacy event created must be a UTC timestamp" });
|
|
1747
|
+
}
|
|
1748
|
+
const normalized = `${match[1]}.${match[2] ?? "000"}Z`;
|
|
1749
|
+
const date = new Date(normalized);
|
|
1750
|
+
if (Number.isNaN(date.getTime()) || date.toISOString() !== normalized) {
|
|
1751
|
+
return err("SCHEME_REJECTED", { message: "legacy event created is not a valid timestamp" });
|
|
1752
|
+
}
|
|
1753
|
+
return ok(normalized);
|
|
1754
|
+
}
|
|
1755
|
+
async function findEventPath(vault, operationId2) {
|
|
1756
|
+
const root = join13(vault, "meta", "log-events");
|
|
1757
|
+
const matches = [];
|
|
1758
|
+
let days;
|
|
1759
|
+
try {
|
|
1760
|
+
days = await readdir3(root, { withFileTypes: true });
|
|
1761
|
+
} catch (error) {
|
|
1762
|
+
return err("SCHEME_REJECTED", {
|
|
1763
|
+
message: "log event root is missing or unreadable",
|
|
1764
|
+
detail: String(error)
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
for (const day2 of days) {
|
|
1768
|
+
if (!day2.isDirectory()) continue;
|
|
1769
|
+
const dayPath = join13(root, day2.name);
|
|
1770
|
+
const entries = await readdir3(dayPath, { withFileTypes: true });
|
|
1771
|
+
for (const entry of entries) {
|
|
1772
|
+
if (!entry.isFile() || entry.name !== `${operationId2}.json`) continue;
|
|
1773
|
+
matches.push({
|
|
1774
|
+
absolute: join13(dayPath, entry.name),
|
|
1775
|
+
relative: `meta/log-events/${day2.name}/${entry.name}`
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (matches.length !== 1) {
|
|
1780
|
+
return err("SCHEME_REJECTED", {
|
|
1781
|
+
message: "event operation ID must resolve to exactly one log event",
|
|
1782
|
+
operation_id: operationId2,
|
|
1783
|
+
matches: matches.map((match) => match.relative)
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
return ok(matches[0]);
|
|
1787
|
+
}
|
|
1788
|
+
function planCanonicalEvent(parsed, operationId2, relativePath, current) {
|
|
1789
|
+
if (!hasExactKeys(parsed, EVENT_KEYS)) {
|
|
1790
|
+
return err("SCHEME_REJECTED", { path: relativePath, message: "canonical event has unexpected fields" });
|
|
1791
|
+
}
|
|
1792
|
+
const validated = validateLogEvent(parsed);
|
|
1793
|
+
if (!validated.ok) return validated;
|
|
1794
|
+
if (validated.data.operation_id !== operationId2 || eventPathFor(validated.data) !== relativePath) {
|
|
1795
|
+
return err("SCHEME_REJECTED", { path: relativePath, message: "path/identity mismatch" });
|
|
1796
|
+
}
|
|
1797
|
+
return ok({ current, repaired: current, needed: false });
|
|
1798
|
+
}
|
|
1799
|
+
function planLegacyEvent(parsed, operationId2, relativePath, current, hostId) {
|
|
1800
|
+
if (!hasExactKeys(parsed, LEGACY_KEYS)) {
|
|
1801
|
+
return err("SCHEME_REJECTED", {
|
|
1802
|
+
path: relativePath,
|
|
1803
|
+
message: "legacy event must contain exactly operation_id, kind, target, note, and created"
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
if (parsed.operation_id !== operationId2) {
|
|
1807
|
+
return err("SCHEME_REJECTED", { path: relativePath, message: "path/identity mismatch" });
|
|
1808
|
+
}
|
|
1809
|
+
const { kind, target, note } = parsed;
|
|
1810
|
+
if (typeof kind !== "string" || typeof target !== "string" || typeof note !== "string") {
|
|
1811
|
+
return err("SCHEME_REJECTED", {
|
|
1812
|
+
path: relativePath,
|
|
1813
|
+
message: "legacy event kind, target, and note must be strings"
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
const occurredAt = normalizeLegacyTimestamp(parsed.created);
|
|
1817
|
+
if (!occurredAt.ok) return occurredAt;
|
|
1818
|
+
const event = {
|
|
1819
|
+
schema: "skillwiki-log-event/v1",
|
|
1820
|
+
operation_id: operationId2,
|
|
1821
|
+
occurred_at: occurredAt.data,
|
|
1822
|
+
host_id: hostId,
|
|
1823
|
+
actor: "skillwiki-cli",
|
|
1824
|
+
kind,
|
|
1825
|
+
target,
|
|
1826
|
+
note,
|
|
1827
|
+
metadata: {}
|
|
1828
|
+
};
|
|
1829
|
+
const validated = validateLogEvent(event);
|
|
1830
|
+
if (!validated.ok) return validated;
|
|
1831
|
+
if (eventPathFor(validated.data) !== relativePath) {
|
|
1832
|
+
return err("SCHEME_REJECTED", { path: relativePath, message: "path/date mismatch" });
|
|
1833
|
+
}
|
|
1834
|
+
return ok({ current, repaired: canonicalEventJson(validated.data), needed: true });
|
|
1835
|
+
}
|
|
1836
|
+
async function planEventRepair(vault, operationId2, hostId) {
|
|
1837
|
+
const located = await findEventPath(vault, operationId2);
|
|
1838
|
+
if (!located.ok) return located;
|
|
1839
|
+
let current;
|
|
1840
|
+
try {
|
|
1841
|
+
current = await readFile4(located.data.absolute, "utf8");
|
|
1842
|
+
} catch (error) {
|
|
1843
|
+
return err("SCHEME_REJECTED", {
|
|
1844
|
+
path: located.data.relative,
|
|
1845
|
+
message: "event is unreadable",
|
|
1846
|
+
detail: String(error)
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
let parsed;
|
|
1850
|
+
try {
|
|
1851
|
+
parsed = JSON.parse(current);
|
|
1852
|
+
} catch {
|
|
1853
|
+
return err("SCHEME_REJECTED", { path: located.data.relative, message: "invalid JSON" });
|
|
1854
|
+
}
|
|
1855
|
+
if (!isPlainObject(parsed)) {
|
|
1856
|
+
return err("SCHEME_REJECTED", { path: located.data.relative, message: "event must be an object" });
|
|
1857
|
+
}
|
|
1858
|
+
const plan = parsed.schema === "skillwiki-log-event/v1" ? planCanonicalEvent(parsed, operationId2, located.data.relative, current) : planLegacyEvent(parsed, operationId2, located.data.relative, current, hostId);
|
|
1859
|
+
if (!plan.ok) return plan;
|
|
1860
|
+
return ok({ ...plan.data, absolutePath: located.data.absolute, relativePath: located.data.relative });
|
|
1861
|
+
}
|
|
1862
|
+
async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
|
|
1863
|
+
if (!OPERATION_ID_RE.test(input.eventOperationId)) {
|
|
1864
|
+
return {
|
|
1865
|
+
exitCode: ExitCode.SCHEME_REJECTED,
|
|
1866
|
+
result: err("SCHEME_REJECTED", { message: "event operation ID must be 64 lowercase hex chars" })
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
let indexText;
|
|
1870
|
+
try {
|
|
1871
|
+
indexText = await readFile4(join13(input.vault, "index.md"), "utf8");
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
return {
|
|
1874
|
+
exitCode: ExitCode.SCHEME_REJECTED,
|
|
1875
|
+
result: err("SCHEME_REJECTED", { path: "index.md", message: "index.md is unreadable", detail: String(error) })
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
const indexPlan = planIndexRepair(indexText);
|
|
1879
|
+
if (!indexPlan.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: indexPlan };
|
|
1880
|
+
const eventPlan = await planEventRepair(
|
|
1881
|
+
input.vault,
|
|
1882
|
+
input.eventOperationId,
|
|
1883
|
+
input.hostId ?? "standalone"
|
|
1884
|
+
);
|
|
1885
|
+
if (!eventPlan.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: eventPlan };
|
|
1886
|
+
const baseOutput = {
|
|
1887
|
+
event_path: eventPlan.data.relativePath,
|
|
1888
|
+
index_repair_needed: indexPlan.data.needed,
|
|
1889
|
+
event_repair_needed: eventPlan.data.needed
|
|
1890
|
+
};
|
|
1891
|
+
if (!input.write) {
|
|
1892
|
+
return {
|
|
1893
|
+
exitCode: ExitCode.OK,
|
|
1894
|
+
result: ok({
|
|
1895
|
+
...baseOutput,
|
|
1896
|
+
index_changed: false,
|
|
1897
|
+
event_changed: false,
|
|
1898
|
+
rolled_back: false,
|
|
1899
|
+
dry_run: true,
|
|
1900
|
+
humanHint: `dry run: index_repair_needed=${indexPlan.data.needed} event_repair_needed=${eventPlan.data.needed}`
|
|
1901
|
+
})
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
let indexChanged = false;
|
|
1905
|
+
if (indexPlan.data.needed) {
|
|
1906
|
+
const written = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.repaired);
|
|
1907
|
+
if (!written.ok) return { exitCode: ExitCode.WRITE_FAILED, result: written };
|
|
1908
|
+
indexChanged = written.data.changed;
|
|
1909
|
+
}
|
|
1910
|
+
let eventChanged = false;
|
|
1911
|
+
if (eventPlan.data.needed) {
|
|
1912
|
+
const written = await deps.writeText(eventPlan.data.absolutePath, eventPlan.data.repaired);
|
|
1913
|
+
if (!written.ok) {
|
|
1914
|
+
let rolledBack = false;
|
|
1915
|
+
let rollbackError;
|
|
1916
|
+
if (indexChanged) {
|
|
1917
|
+
const rollback = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.current);
|
|
1918
|
+
rolledBack = rollback.ok;
|
|
1919
|
+
if (!rollback.ok) rollbackError = rollback;
|
|
1920
|
+
}
|
|
1921
|
+
return {
|
|
1922
|
+
exitCode: ExitCode.WRITE_FAILED,
|
|
1923
|
+
result: err("WRITE_FAILED", {
|
|
1924
|
+
message: "event repair failed",
|
|
1925
|
+
cause: written,
|
|
1926
|
+
rolled_back: rolledBack,
|
|
1927
|
+
rollback_error: rollbackError
|
|
1928
|
+
})
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
eventChanged = written.data.changed;
|
|
1932
|
+
}
|
|
1933
|
+
return {
|
|
1934
|
+
exitCode: ExitCode.OK,
|
|
1935
|
+
result: ok({
|
|
1936
|
+
...baseOutput,
|
|
1937
|
+
index_changed: indexChanged,
|
|
1938
|
+
event_changed: eventChanged,
|
|
1939
|
+
rolled_back: false,
|
|
1940
|
+
dry_run: false,
|
|
1941
|
+
humanHint: `repaired legacy projections index_changed=${indexChanged} event_changed=${eventChanged}`
|
|
1942
|
+
})
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1686
1946
|
// src/commands/claim.ts
|
|
1687
|
-
import { mkdir as mkdir4, writeFile as writeFile3, readFile as
|
|
1947
|
+
import { mkdir as mkdir4, writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
|
|
1688
1948
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
1689
|
-
import { join as
|
|
1949
|
+
import { join as join14 } from "path";
|
|
1690
1950
|
function extractDate(filename) {
|
|
1691
1951
|
const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
1692
1952
|
return m?.[1] ?? "";
|
|
@@ -1701,11 +1961,11 @@ async function runClaim(input) {
|
|
|
1701
1961
|
if (!existsSync2(input.vault) || !statSync(input.vault).isDirectory()) {
|
|
1702
1962
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
|
|
1703
1963
|
}
|
|
1704
|
-
const absTranscript =
|
|
1964
|
+
const absTranscript = join14(input.vault, input.transcript);
|
|
1705
1965
|
if (!existsSync2(absTranscript)) {
|
|
1706
1966
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
|
|
1707
1967
|
}
|
|
1708
|
-
const content = await
|
|
1968
|
+
const content = await readFile5(absTranscript, "utf8");
|
|
1709
1969
|
const fm = extractFrontmatter(content);
|
|
1710
1970
|
let projectSlug = input.project;
|
|
1711
1971
|
if (!projectSlug && fm.ok && typeof fm.data.project === "string") {
|
|
@@ -1717,7 +1977,7 @@ async function runClaim(input) {
|
|
|
1717
1977
|
result: err("SCHEME_REJECTED", { message: "No project specified. Use --project or set project in transcript frontmatter." })
|
|
1718
1978
|
};
|
|
1719
1979
|
}
|
|
1720
|
-
const projectDir =
|
|
1980
|
+
const projectDir = join14(input.vault, "projects", projectSlug);
|
|
1721
1981
|
if (!existsSync2(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
1722
1982
|
return {
|
|
1723
1983
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
@@ -1734,7 +1994,7 @@ async function runClaim(input) {
|
|
|
1734
1994
|
}
|
|
1735
1995
|
const workSlug = input.slug || extractSlugFromFilename(filename);
|
|
1736
1996
|
const dirName = `${date}-${workSlug}`;
|
|
1737
|
-
const workDir =
|
|
1997
|
+
const workDir = join14(projectDir, "work", dirName);
|
|
1738
1998
|
const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
|
|
1739
1999
|
const relSpecPath = `${relWorkDir}/spec.md`;
|
|
1740
2000
|
if (existsSync2(workDir)) {
|
|
@@ -1763,7 +2023,7 @@ async function runClaim(input) {
|
|
|
1763
2023
|
`Claimed from ${input.transcript}`,
|
|
1764
2024
|
""
|
|
1765
2025
|
];
|
|
1766
|
-
await writeFile3(
|
|
2026
|
+
await writeFile3(join14(workDir, "spec.md"), specLines.join("\n"), "utf8");
|
|
1767
2027
|
appendLastOp(input.vault, {
|
|
1768
2028
|
operation: "claim",
|
|
1769
2029
|
summary: `claimed ${input.transcript} \u2192 ${relWorkDir}`,
|
|
@@ -1783,11 +2043,11 @@ async function runClaim(input) {
|
|
|
1783
2043
|
|
|
1784
2044
|
// src/commands/work-complete.ts
|
|
1785
2045
|
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync10, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1786
|
-
import { join as
|
|
2046
|
+
import { join as join16 } from "path";
|
|
1787
2047
|
|
|
1788
2048
|
// src/commands/work-validate.ts
|
|
1789
2049
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync9 } from "fs";
|
|
1790
|
-
import { join as
|
|
2050
|
+
import { join as join15 } from "path";
|
|
1791
2051
|
function normalizeWorkItemRel(workItem) {
|
|
1792
2052
|
return workItem.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
1793
2053
|
}
|
|
@@ -1844,7 +2104,7 @@ function validatePrMetadata(meta, findings) {
|
|
|
1844
2104
|
}
|
|
1845
2105
|
async function runWorkValidate(input) {
|
|
1846
2106
|
const rel = normalizeWorkItemRel(input.workItem);
|
|
1847
|
-
const workDir =
|
|
2107
|
+
const workDir = join15(input.vault, rel);
|
|
1848
2108
|
if (!existsSync3(workDir)) {
|
|
1849
2109
|
return {
|
|
1850
2110
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -1863,7 +2123,7 @@ async function runWorkValidate(input) {
|
|
|
1863
2123
|
findings.push({ code: "missing_spec", path: `${rel}/spec.md`, message: "spec.md is required" });
|
|
1864
2124
|
}
|
|
1865
2125
|
for (const file of files) {
|
|
1866
|
-
const abs =
|
|
2126
|
+
const abs = join15(workDir, file);
|
|
1867
2127
|
let text;
|
|
1868
2128
|
try {
|
|
1869
2129
|
text = readFileSync9(abs, "utf8");
|
|
@@ -1960,7 +2220,7 @@ async function runWorkValidate(input) {
|
|
|
1960
2220
|
|
|
1961
2221
|
// src/commands/work-complete.ts
|
|
1962
2222
|
function workCompleteJournalPath(vault, opId) {
|
|
1963
|
-
return
|
|
2223
|
+
return join16(vault, ".skillwiki", "work-complete", `${opId}.env`);
|
|
1964
2224
|
}
|
|
1965
2225
|
function readWorkJournal(vault, opId) {
|
|
1966
2226
|
const path = workCompleteJournalPath(vault, opId);
|
|
@@ -1973,7 +2233,7 @@ function readWorkJournal(vault, opId) {
|
|
|
1973
2233
|
}
|
|
1974
2234
|
function writeWorkJournal(vault, opId, fields) {
|
|
1975
2235
|
const path = workCompleteJournalPath(vault, opId);
|
|
1976
|
-
const dir =
|
|
2236
|
+
const dir = join16(vault, ".skillwiki", "work-complete");
|
|
1977
2237
|
mkdirSync(dir, { recursive: true });
|
|
1978
2238
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
1979
2239
|
const body = serializeJournalEnv(fields, [
|
|
@@ -1990,11 +2250,11 @@ function writeWorkJournal(vault, opId, fields) {
|
|
|
1990
2250
|
var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"];
|
|
1991
2251
|
function resolveWorkDir(vault, workItem) {
|
|
1992
2252
|
const rel = normalizeWorkItemRel(workItem);
|
|
1993
|
-
const abs =
|
|
2253
|
+
const abs = join16(vault, rel);
|
|
1994
2254
|
if (!existsSync4(abs)) {
|
|
1995
2255
|
return err("FILE_NOT_FOUND", { path: rel });
|
|
1996
2256
|
}
|
|
1997
|
-
if (!existsSync4(
|
|
2257
|
+
if (!existsSync4(join16(abs, "spec.md"))) {
|
|
1998
2258
|
return err("PREFLIGHT_FAILED", { reason: "missing-spec", path: rel });
|
|
1999
2259
|
}
|
|
2000
2260
|
return ok(abs);
|
|
@@ -2042,7 +2302,7 @@ async function setStatusCompleted(filePath) {
|
|
|
2042
2302
|
return patchFrontmatterStatus(filePath);
|
|
2043
2303
|
}
|
|
2044
2304
|
async function writeEvidence(workDir, opId, phases) {
|
|
2045
|
-
const path =
|
|
2305
|
+
const path = join16(workDir, "evidence.md");
|
|
2046
2306
|
const body = [
|
|
2047
2307
|
"---",
|
|
2048
2308
|
"title: work-complete evidence",
|
|
@@ -2060,7 +2320,7 @@ async function writeEvidence(workDir, opId, phases) {
|
|
|
2060
2320
|
return atomicWriteText(path, body);
|
|
2061
2321
|
}
|
|
2062
2322
|
async function markPlanComplete(workDir) {
|
|
2063
|
-
const planPath =
|
|
2323
|
+
const planPath = join16(workDir, "plan.md");
|
|
2064
2324
|
if (!existsSync4(planPath)) return ok(null);
|
|
2065
2325
|
return patchFrontmatterStatus(planPath, (body) => body.replace(/- \[ \]/g, "- [x]"));
|
|
2066
2326
|
}
|
|
@@ -2147,7 +2407,7 @@ async function runWorkComplete(input) {
|
|
|
2147
2407
|
advance("evidence");
|
|
2148
2408
|
}
|
|
2149
2409
|
if (phaseIndex(phase) <= phaseIndex("evidence")) {
|
|
2150
|
-
const statusWrite = await setStatusCompleted(
|
|
2410
|
+
const statusWrite = await setStatusCompleted(join16(workDir, "spec.md"));
|
|
2151
2411
|
if (!statusWrite.ok) return writeFailure("evidence-spec", statusWrite);
|
|
2152
2412
|
const planWrite = await markPlanComplete(workDir);
|
|
2153
2413
|
if (!planWrite.ok) return writeFailure("evidence-plan", planWrite);
|
|
@@ -2181,7 +2441,7 @@ async function runWorkComplete(input) {
|
|
|
2181
2441
|
advance("projection");
|
|
2182
2442
|
}
|
|
2183
2443
|
if (phaseIndex(phase) <= phaseIndex("projection")) {
|
|
2184
|
-
if (!existsSync4(
|
|
2444
|
+
if (!existsSync4(join16(workDir, "evidence.md"))) {
|
|
2185
2445
|
const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
|
|
2186
2446
|
if (!evidenceWrite.ok) return writeFailure("projection-evidence", evidenceWrite);
|
|
2187
2447
|
}
|
|
@@ -2206,7 +2466,7 @@ async function runWorkComplete(input) {
|
|
|
2206
2466
|
}
|
|
2207
2467
|
let committed = false;
|
|
2208
2468
|
if (phaseIndex(phase) <= phaseIndex("commit")) {
|
|
2209
|
-
if (!input.noCommit && existsSync4(
|
|
2469
|
+
if (!input.noCommit && existsSync4(join16(input.vault, ".git"))) {
|
|
2210
2470
|
try {
|
|
2211
2471
|
appendLastOp(input.vault, {
|
|
2212
2472
|
operation: "work-complete",
|
|
@@ -2267,7 +2527,7 @@ async function runWorkComplete(input) {
|
|
|
2267
2527
|
|
|
2268
2528
|
// src/commands/health.ts
|
|
2269
2529
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
2270
|
-
import { dirname as dirname4, join as
|
|
2530
|
+
import { dirname as dirname4, join as join17, resolve as resolve2 } from "path";
|
|
2271
2531
|
import { platform } from "os";
|
|
2272
2532
|
function statusFromCounts(counts) {
|
|
2273
2533
|
if ((counts.error ?? 0) > 0) return "error";
|
|
@@ -2400,11 +2660,11 @@ function runVaultSyncHealth(home, syncMode) {
|
|
|
2400
2660
|
};
|
|
2401
2661
|
}
|
|
2402
2662
|
const isMac = platform() === "darwin";
|
|
2403
|
-
const shareDir = isMac ?
|
|
2404
|
-
const logDir = isMac ?
|
|
2405
|
-
const filterPath =
|
|
2663
|
+
const shareDir = isMac ? join17(home, "Library", "Application Support", "vault-sync", "bin") : join17(home, ".local", "share", "vault-sync", "bin");
|
|
2664
|
+
const logDir = isMac ? join17(home, "Library", "Logs") : join17(home, ".local", "state", "vault-sync", "log");
|
|
2665
|
+
const filterPath = join17(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
2406
2666
|
const checks = [];
|
|
2407
|
-
const pushScript =
|
|
2667
|
+
const pushScript = join17(shareDir, "wiki-push.sh");
|
|
2408
2668
|
if (syncMode === "optional" && !existsSync5(pushScript)) {
|
|
2409
2669
|
return {
|
|
2410
2670
|
status: "pass",
|
|
@@ -2420,20 +2680,20 @@ function runVaultSyncHealth(home, syncMode) {
|
|
|
2420
2680
|
}
|
|
2421
2681
|
checks.push(existsSync5(pushScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${pushScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${pushScript}` });
|
|
2422
2682
|
if (isMac) {
|
|
2423
|
-
const pushPlist =
|
|
2424
|
-
const fetchPlist =
|
|
2683
|
+
const pushPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
|
|
2684
|
+
const fetchPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
|
|
2425
2685
|
checks.push(existsSync5(pushPlist) && existsSync5(fetchPlist) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "launchd unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "launchd unit files missing (read-only mode)" });
|
|
2426
2686
|
checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
|
|
2427
2687
|
} else {
|
|
2428
|
-
const pushTimer =
|
|
2429
|
-
const fetchTimer =
|
|
2430
|
-
const fuseTimer =
|
|
2431
|
-
const fuseService =
|
|
2688
|
+
const pushTimer = join17(home, ".config", "systemd", "user", "wiki-push.timer");
|
|
2689
|
+
const fetchTimer = join17(home, ".config", "systemd", "user", "wiki-fetch.timer");
|
|
2690
|
+
const fuseTimer = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
|
|
2691
|
+
const fuseService = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
|
|
2432
2692
|
checks.push(existsSync5(pushTimer) && existsSync5(fetchTimer) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "systemd timer unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "systemd timer unit files missing (read-only mode)" });
|
|
2433
2693
|
checks.push(existsSync5(fuseTimer) && existsSync5(fuseService) ? { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "wiki-fuse-refresh unit files present (read-only mode)" } : { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "warn", detail: "wiki-fuse-refresh unit files missing (read-only mode)" });
|
|
2434
2694
|
}
|
|
2435
|
-
checks.push(classifyLog(
|
|
2436
|
-
checks.push(classifyLog(
|
|
2695
|
+
checks.push(classifyLog(join17(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
|
|
2696
|
+
checks.push(classifyLog(join17(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
|
|
2437
2697
|
if (!existsSync5(filterPath)) {
|
|
2438
2698
|
checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
|
|
2439
2699
|
} else {
|
|
@@ -2756,12 +3016,12 @@ async function runHealth(input) {
|
|
|
2756
3016
|
}
|
|
2757
3017
|
|
|
2758
3018
|
// src/commands/archive.ts
|
|
2759
|
-
import { rename as rename2, mkdir as mkdir6, readFile as
|
|
2760
|
-
import { join as
|
|
3019
|
+
import { rename as rename2, mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
|
|
3020
|
+
import { join as join19, dirname as dirname5 } from "path";
|
|
2761
3021
|
|
|
2762
3022
|
// src/utils/delete-intent.ts
|
|
2763
|
-
import { mkdir as mkdir5, writeFile as writeFile4, readdir as
|
|
2764
|
-
import { join as
|
|
3023
|
+
import { mkdir as mkdir5, writeFile as writeFile4, readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
3024
|
+
import { join as join18 } from "path";
|
|
2765
3025
|
var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
|
|
2766
3026
|
var DELETE_INTENT_DIR = "meta/delete-intents";
|
|
2767
3027
|
function normalizeVaultRelPath(path) {
|
|
@@ -2792,10 +3052,10 @@ function buildDeleteIntent(input) {
|
|
|
2792
3052
|
};
|
|
2793
3053
|
}
|
|
2794
3054
|
async function writeDeleteIntent(vault, intent) {
|
|
2795
|
-
const dir =
|
|
3055
|
+
const dir = join18(vault, DELETE_INTENT_DIR);
|
|
2796
3056
|
await mkdir5(dir, { recursive: true });
|
|
2797
3057
|
const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
|
|
2798
|
-
await writeFile4(
|
|
3058
|
+
await writeFile4(join18(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
|
|
2799
3059
|
return rel;
|
|
2800
3060
|
}
|
|
2801
3061
|
|
|
@@ -2833,7 +3093,7 @@ async function runArchive(input) {
|
|
|
2833
3093
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
2834
3094
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
2835
3095
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
2836
|
-
const archivePath =
|
|
3096
|
+
const archivePath = join19("_archive", relPath).replace(/\\/g, "/");
|
|
2837
3097
|
const remoteRoot = normalizeRemoteRoot(input.remote);
|
|
2838
3098
|
const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
|
|
2839
3099
|
let cascade;
|
|
@@ -2859,7 +3119,7 @@ async function runArchive(input) {
|
|
|
2859
3119
|
const indexRefs = [];
|
|
2860
3120
|
if (!isRaw) {
|
|
2861
3121
|
try {
|
|
2862
|
-
const idx = await
|
|
3122
|
+
const idx = await readFile7(join19(input.vault, "index.md"), "utf8");
|
|
2863
3123
|
idx.split("\n").forEach((line, i) => {
|
|
2864
3124
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
2865
3125
|
});
|
|
@@ -2886,8 +3146,8 @@ async function runArchive(input) {
|
|
|
2886
3146
|
}
|
|
2887
3147
|
if (input.cascade && input.apply && cascade) {
|
|
2888
3148
|
for (const ref of cascade.source_array_refs) {
|
|
2889
|
-
const absPath =
|
|
2890
|
-
const text = await
|
|
3149
|
+
const absPath = join19(input.vault, ref.page);
|
|
3150
|
+
const text = await readFile7(absPath, "utf8");
|
|
2891
3151
|
const split = splitFrontmatter(text);
|
|
2892
3152
|
if (!split.ok) continue;
|
|
2893
3153
|
const before = split.data.rawFrontmatter;
|
|
@@ -2904,12 +3164,12 @@ ${fmRewritten}
|
|
|
2904
3164
|
}
|
|
2905
3165
|
}
|
|
2906
3166
|
}
|
|
2907
|
-
await mkdir6(dirname5(
|
|
2908
|
-
await rename2(
|
|
3167
|
+
await mkdir6(dirname5(join19(input.vault, archivePath)), { recursive: true });
|
|
3168
|
+
await rename2(join19(input.vault, relPath), join19(input.vault, archivePath));
|
|
2909
3169
|
let indexUpdated = false;
|
|
2910
3170
|
if (!isRaw) {
|
|
2911
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
2912
|
-
const before = await
|
|
3171
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
|
|
3172
|
+
const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
|
|
2913
3173
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
2914
3174
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
2915
3175
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -2960,7 +3220,7 @@ ${fmRewritten}
|
|
|
2960
3220
|
|
|
2961
3221
|
// src/commands/remove.ts
|
|
2962
3222
|
import { unlink as unlink2, access } from "fs/promises";
|
|
2963
|
-
import { join as
|
|
3223
|
+
import { join as join20 } from "path";
|
|
2964
3224
|
async function pathExists(abs) {
|
|
2965
3225
|
try {
|
|
2966
3226
|
await access(abs);
|
|
@@ -2986,7 +3246,7 @@ async function runRemove(input) {
|
|
|
2986
3246
|
if (!relPath) {
|
|
2987
3247
|
try {
|
|
2988
3248
|
const candidate = normalizeVaultRelPath(input.page);
|
|
2989
|
-
if (await pathExists(
|
|
3249
|
+
if (await pathExists(join20(input.vault, candidate))) {
|
|
2990
3250
|
relPath = candidate;
|
|
2991
3251
|
}
|
|
2992
3252
|
} catch {
|
|
@@ -3015,12 +3275,12 @@ async function runRemove(input) {
|
|
|
3015
3275
|
reason: input.reason
|
|
3016
3276
|
});
|
|
3017
3277
|
const tombstonePath = await writeDeleteIntent(input.vault, intent);
|
|
3018
|
-
await unlink2(
|
|
3278
|
+
await unlink2(join20(input.vault, relPath));
|
|
3019
3279
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
3020
|
-
const { readFile:
|
|
3280
|
+
const { readFile: readFile17 } = await import("fs/promises");
|
|
3021
3281
|
const { join: pathJoin } = await import("path");
|
|
3022
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
3023
|
-
const before = await
|
|
3282
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
|
|
3283
|
+
const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
3024
3284
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
3025
3285
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
3026
3286
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -3368,7 +3628,7 @@ ${migratedBody}${newFooter}`;
|
|
|
3368
3628
|
|
|
3369
3629
|
// src/commands/update.ts
|
|
3370
3630
|
import { execSync } from "child_process";
|
|
3371
|
-
import { join as
|
|
3631
|
+
import { join as join21 } from "path";
|
|
3372
3632
|
function parseCoreSemver(version) {
|
|
3373
3633
|
const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
3374
3634
|
if (!m) return null;
|
|
@@ -3399,7 +3659,7 @@ function resolveGlobalSkillsRoot() {
|
|
|
3399
3659
|
encoding: "utf8",
|
|
3400
3660
|
timeout: 5e3
|
|
3401
3661
|
}).trim();
|
|
3402
|
-
return
|
|
3662
|
+
return join21(globalRoot, "skillwiki", "skills");
|
|
3403
3663
|
} catch {
|
|
3404
3664
|
return null;
|
|
3405
3665
|
}
|
|
@@ -3427,7 +3687,7 @@ async function runUpdate(input) {
|
|
|
3427
3687
|
const pkg2 = readCliPackageJson();
|
|
3428
3688
|
const currentVersion = pkg2.version;
|
|
3429
3689
|
const tag = normalizeDistTag(input.distTag);
|
|
3430
|
-
const target =
|
|
3690
|
+
const target = join21(input.home, ".claude", "skills");
|
|
3431
3691
|
let latest;
|
|
3432
3692
|
try {
|
|
3433
3693
|
latest = execSync(`npm view skillwiki@${tag} version`, {
|
|
@@ -3508,13 +3768,13 @@ async function runUpdate(input) {
|
|
|
3508
3768
|
// src/commands/self-update.ts
|
|
3509
3769
|
import { execSync as execSync2 } from "child_process";
|
|
3510
3770
|
import { existsSync as existsSync6, readFileSync as readFileSync12 } from "fs";
|
|
3511
|
-
import { join as
|
|
3771
|
+
import { join as join22 } from "path";
|
|
3512
3772
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
3513
3773
|
async function runSelfUpdate(input) {
|
|
3514
3774
|
const currentVersion = readCliPackageJson().version;
|
|
3515
3775
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
3516
3776
|
const distTag = normalizeDistTag(input.distTag);
|
|
3517
|
-
const localPkgPath =
|
|
3777
|
+
const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
|
|
3518
3778
|
const hasLocalSource = existsSync6(localPkgPath);
|
|
3519
3779
|
if (input.check) {
|
|
3520
3780
|
let availableVersion = null;
|
|
@@ -3646,21 +3906,21 @@ async function runSelfUpdate(input) {
|
|
|
3646
3906
|
}
|
|
3647
3907
|
|
|
3648
3908
|
// src/commands/transcripts.ts
|
|
3649
|
-
import { readdir as
|
|
3650
|
-
import { join as
|
|
3909
|
+
import { readdir as readdir5, stat as stat3, readFile as readFile9 } from "fs/promises";
|
|
3910
|
+
import { join as join23 } from "path";
|
|
3651
3911
|
async function runTranscripts(input) {
|
|
3652
|
-
const dir =
|
|
3912
|
+
const dir = join23(input.vault, "raw", "transcripts");
|
|
3653
3913
|
let entries;
|
|
3654
3914
|
try {
|
|
3655
|
-
entries = await
|
|
3915
|
+
entries = await readdir5(dir, { withFileTypes: true });
|
|
3656
3916
|
} catch {
|
|
3657
3917
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: { ok: false, error: "VAULT_PATH_INVALID", detail: `raw/transcripts/ not found: ${dir}` } };
|
|
3658
3918
|
}
|
|
3659
3919
|
const transcripts = [];
|
|
3660
3920
|
for (const entry of entries) {
|
|
3661
3921
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
3662
|
-
const filePath =
|
|
3663
|
-
const content = await
|
|
3922
|
+
const filePath = join23(dir, entry.name);
|
|
3923
|
+
const content = await readFile9(filePath, "utf8");
|
|
3664
3924
|
const fm = extractFrontmatter(content);
|
|
3665
3925
|
if (!fm.ok) continue;
|
|
3666
3926
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -3677,10 +3937,10 @@ async function runTranscripts(input) {
|
|
|
3677
3937
|
}
|
|
3678
3938
|
|
|
3679
3939
|
// src/commands/compound.ts
|
|
3680
|
-
import { writeFile as writeFile7, mkdir as mkdir7, readdir as
|
|
3681
|
-
import { join as
|
|
3940
|
+
import { writeFile as writeFile7, mkdir as mkdir7, readdir as readdir6, unlink as unlink3 } from "fs/promises";
|
|
3941
|
+
import { join as join24 } from "path";
|
|
3682
3942
|
import { existsSync as existsSync7 } from "fs";
|
|
3683
|
-
import { readFile as
|
|
3943
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
3684
3944
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
3685
3945
|
var FIELD_RE = {
|
|
3686
3946
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -3778,17 +4038,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
3778
4038
|
};
|
|
3779
4039
|
}
|
|
3780
4040
|
async function runCompound(input) {
|
|
3781
|
-
const logPath =
|
|
4041
|
+
const logPath = join24(input.vault, "log.md");
|
|
3782
4042
|
let logText;
|
|
3783
4043
|
try {
|
|
3784
|
-
logText = await
|
|
4044
|
+
logText = await readFile10(logPath, "utf8");
|
|
3785
4045
|
} catch {
|
|
3786
4046
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
3787
4047
|
}
|
|
3788
4048
|
const entries = parseRetroEntries(logText);
|
|
3789
4049
|
const promoted = [];
|
|
3790
4050
|
const skipped = [];
|
|
3791
|
-
const compoundDir =
|
|
4051
|
+
const compoundDir = join24(input.vault, "projects", input.project, "compound");
|
|
3792
4052
|
for (const entry of entries) {
|
|
3793
4053
|
const generalizeValue = entry.generalize.trim();
|
|
3794
4054
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -3796,7 +4056,7 @@ async function runCompound(input) {
|
|
|
3796
4056
|
continue;
|
|
3797
4057
|
}
|
|
3798
4058
|
const slug = slugify(entry.cycleName);
|
|
3799
|
-
const compoundPath =
|
|
4059
|
+
const compoundPath = join24(compoundDir, `${slug}.md`);
|
|
3800
4060
|
if (existsSync7(compoundPath)) {
|
|
3801
4061
|
skipped.push(entry.date);
|
|
3802
4062
|
continue;
|
|
@@ -3857,7 +4117,7 @@ async function runCompound(input) {
|
|
|
3857
4117
|
};
|
|
3858
4118
|
}
|
|
3859
4119
|
async function runCompoundDelete(input) {
|
|
3860
|
-
const projectDir =
|
|
4120
|
+
const projectDir = join24(input.vault, "projects", input.project);
|
|
3861
4121
|
if (!existsSync7(projectDir)) {
|
|
3862
4122
|
return {
|
|
3863
4123
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
@@ -3865,7 +4125,7 @@ async function runCompoundDelete(input) {
|
|
|
3865
4125
|
};
|
|
3866
4126
|
}
|
|
3867
4127
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
3868
|
-
const compoundPath =
|
|
4128
|
+
const compoundPath = join24(projectDir, "compound", `${entryName}.md`);
|
|
3869
4129
|
if (!existsSync7(compoundPath)) {
|
|
3870
4130
|
return {
|
|
3871
4131
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -3899,7 +4159,7 @@ knowledge.md regenerated`
|
|
|
3899
4159
|
};
|
|
3900
4160
|
}
|
|
3901
4161
|
async function runCompoundList(input) {
|
|
3902
|
-
const compoundDir =
|
|
4162
|
+
const compoundDir = join24(input.vault, "projects", input.project, "compound");
|
|
3903
4163
|
if (!existsSync7(compoundDir)) {
|
|
3904
4164
|
return {
|
|
3905
4165
|
exitCode: ExitCode.OK,
|
|
@@ -3914,7 +4174,7 @@ no compound directory found`
|
|
|
3914
4174
|
}
|
|
3915
4175
|
let dirents;
|
|
3916
4176
|
try {
|
|
3917
|
-
dirents = await
|
|
4177
|
+
dirents = await readdir6(compoundDir, { withFileTypes: true });
|
|
3918
4178
|
} catch {
|
|
3919
4179
|
return {
|
|
3920
4180
|
exitCode: ExitCode.OK,
|
|
@@ -3930,10 +4190,10 @@ could not read compound directory`
|
|
|
3930
4190
|
const entries = [];
|
|
3931
4191
|
for (const dirent of dirents) {
|
|
3932
4192
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
3933
|
-
const filePath =
|
|
4193
|
+
const filePath = join24(compoundDir, dirent.name);
|
|
3934
4194
|
let text;
|
|
3935
4195
|
try {
|
|
3936
|
-
text = await
|
|
4196
|
+
text = await readFile10(filePath, "utf8");
|
|
3937
4197
|
} catch {
|
|
3938
4198
|
continue;
|
|
3939
4199
|
}
|
|
@@ -3962,8 +4222,8 @@ no compound entries found`;
|
|
|
3962
4222
|
}
|
|
3963
4223
|
|
|
3964
4224
|
// src/commands/session-brief.ts
|
|
3965
|
-
import { mkdir as mkdir8, readFile as
|
|
3966
|
-
import { join as
|
|
4225
|
+
import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
|
|
4226
|
+
import { join as join25, relative, sep } from "path";
|
|
3967
4227
|
var MAX_WORDS = 900;
|
|
3968
4228
|
async function runSessionBrief(input) {
|
|
3969
4229
|
const scan = await scanVault(input.vault);
|
|
@@ -4063,7 +4323,7 @@ async function resolveProject(input) {
|
|
|
4063
4323
|
const envProject = input.env?.SKILLWIKI_PROJECT;
|
|
4064
4324
|
if (envProject) return envProject;
|
|
4065
4325
|
const cwd = input.cwd ?? process.cwd();
|
|
4066
|
-
const projectDotenv = await readProjectSlug(
|
|
4326
|
+
const projectDotenv = await readProjectSlug(join25(cwd, ".skillwiki", ".env"));
|
|
4067
4327
|
if (projectDotenv) return projectDotenv;
|
|
4068
4328
|
const inferred = inferProjectFromPath(input.vault, cwd);
|
|
4069
4329
|
if (inferred) return inferred;
|
|
@@ -4072,7 +4332,7 @@ async function resolveProject(input) {
|
|
|
4072
4332
|
async function readProjectSlug(file) {
|
|
4073
4333
|
let text;
|
|
4074
4334
|
try {
|
|
4075
|
-
text = await
|
|
4335
|
+
text = await readFile11(file, "utf8");
|
|
4076
4336
|
} catch {
|
|
4077
4337
|
return void 0;
|
|
4078
4338
|
}
|
|
@@ -4149,7 +4409,7 @@ async function loadTrendDigests(typedPages) {
|
|
|
4149
4409
|
return out;
|
|
4150
4410
|
}
|
|
4151
4411
|
async function loadSessionPins(vault, project) {
|
|
4152
|
-
const text = await readIfExists(
|
|
4412
|
+
const text = await readIfExists(join25(vault, "meta", "session-pins.md"));
|
|
4153
4413
|
if (!text) return [];
|
|
4154
4414
|
const fm = extractFrontmatter(text);
|
|
4155
4415
|
if (!fm.ok) return [];
|
|
@@ -4266,7 +4526,7 @@ function satelliteHealthWarnings(warning) {
|
|
|
4266
4526
|
return warning ? [warning] : [];
|
|
4267
4527
|
}
|
|
4268
4528
|
async function loadHealthWarnings(vault) {
|
|
4269
|
-
const text = await readIfExists(
|
|
4529
|
+
const text = await readIfExists(join25(vault, ".skillwiki", "health.json"));
|
|
4270
4530
|
if (!text) return [];
|
|
4271
4531
|
try {
|
|
4272
4532
|
const parsed = JSON.parse(text);
|
|
@@ -4279,7 +4539,7 @@ async function loadHealthWarnings(vault) {
|
|
|
4279
4539
|
}
|
|
4280
4540
|
}
|
|
4281
4541
|
async function loadMemoryTopics(vault, project) {
|
|
4282
|
-
const text = await readIfExists(
|
|
4542
|
+
const text = await readIfExists(join25(vault, ".skillwiki", "memory", project, "topics.json"));
|
|
4283
4543
|
if (!text) return [];
|
|
4284
4544
|
try {
|
|
4285
4545
|
const parsed = JSON.parse(text);
|
|
@@ -4304,11 +4564,11 @@ async function loadMemoryTopics(vault, project) {
|
|
|
4304
4564
|
}
|
|
4305
4565
|
}
|
|
4306
4566
|
async function writeBriefArtifacts(vault, input) {
|
|
4307
|
-
const metaPath =
|
|
4308
|
-
const cacheMdPath =
|
|
4309
|
-
const cacheJsonPath =
|
|
4310
|
-
await mkdir8(
|
|
4311
|
-
await mkdir8(
|
|
4567
|
+
const metaPath = join25(vault, "meta", "latest-session-brief.md");
|
|
4568
|
+
const cacheMdPath = join25(vault, ".skillwiki", "session-brief.md");
|
|
4569
|
+
const cacheJsonPath = join25(vault, ".skillwiki", "session-brief.json");
|
|
4570
|
+
await mkdir8(join25(vault, "meta"), { recursive: true });
|
|
4571
|
+
await mkdir8(join25(vault, ".skillwiki"), { recursive: true });
|
|
4312
4572
|
const committed = renderCommittedBrief(input);
|
|
4313
4573
|
const previousComparable = comparableBrief(await readIfExists(metaPath));
|
|
4314
4574
|
const nextComparable = comparableBrief(committed);
|
|
@@ -4378,7 +4638,7 @@ function renderCommittedBrief(input) {
|
|
|
4378
4638
|
].filter((line) => line !== "").join("\n");
|
|
4379
4639
|
}
|
|
4380
4640
|
async function rebuildRootIndexProjection(vault) {
|
|
4381
|
-
const before = await readIfExists(
|
|
4641
|
+
const before = await readIfExists(join25(vault, "index.md"));
|
|
4382
4642
|
if (!before) return false;
|
|
4383
4643
|
const projection = await renderRootIndex({ vault, currentText: before });
|
|
4384
4644
|
if (!projection.ok) return false;
|
|
@@ -4388,7 +4648,7 @@ async function rebuildRootIndexProjection(vault) {
|
|
|
4388
4648
|
}
|
|
4389
4649
|
async function readIfExists(path) {
|
|
4390
4650
|
try {
|
|
4391
|
-
return await
|
|
4651
|
+
return await readFile11(path, "utf8");
|
|
4392
4652
|
} catch {
|
|
4393
4653
|
return "";
|
|
4394
4654
|
}
|
|
@@ -4430,14 +4690,14 @@ function dateFromPath(path) {
|
|
|
4430
4690
|
}
|
|
4431
4691
|
|
|
4432
4692
|
// src/commands/ingest.ts
|
|
4433
|
-
import { readFile as
|
|
4434
|
-
import { join as
|
|
4693
|
+
import { readFile as readFile13, open, unlink as unlink4, mkdir as mkdir9 } from "fs/promises";
|
|
4694
|
+
import { join as join27 } from "path";
|
|
4435
4695
|
import { createHash as createHash4 } from "crypto";
|
|
4436
4696
|
|
|
4437
4697
|
// src/commands/page-publish.ts
|
|
4438
4698
|
import { realpathSync } from "fs";
|
|
4439
|
-
import { readFile as
|
|
4440
|
-
import { join as
|
|
4699
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
4700
|
+
import { join as join26, resolve as resolve3 } from "path";
|
|
4441
4701
|
var DEFAULT_DEPS = {
|
|
4442
4702
|
afterStage: async () => void 0,
|
|
4443
4703
|
preflight: (input) => runManagedWritePreflight(input)
|
|
@@ -4507,7 +4767,7 @@ function preparePagePublicationFromContent(input) {
|
|
|
4507
4767
|
async function preparePagePublication(input) {
|
|
4508
4768
|
let content;
|
|
4509
4769
|
try {
|
|
4510
|
-
content = await
|
|
4770
|
+
content = await readFile12(input.draftPath, "utf8");
|
|
4511
4771
|
} catch (error) {
|
|
4512
4772
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
4513
4773
|
}
|
|
@@ -4578,10 +4838,10 @@ async function runLockedPrimaryStages(input, vault, deps) {
|
|
|
4578
4838
|
ExitCode.VAULT_PATH_INVALID
|
|
4579
4839
|
);
|
|
4580
4840
|
}
|
|
4581
|
-
const schemaPath =
|
|
4841
|
+
const schemaPath = join26(vault, "SCHEMA.md");
|
|
4582
4842
|
let schemaText;
|
|
4583
4843
|
try {
|
|
4584
|
-
schemaText = await
|
|
4844
|
+
schemaText = await readFile12(schemaPath, "utf8");
|
|
4585
4845
|
} catch (error) {
|
|
4586
4846
|
return lockedFailure("schema", state, err("WRITE_FAILED", { message: String(error) }));
|
|
4587
4847
|
}
|
|
@@ -4611,8 +4871,8 @@ async function runLockedPrimaryStages(input, vault, deps) {
|
|
|
4611
4871
|
let visibleSchema;
|
|
4612
4872
|
try {
|
|
4613
4873
|
[visible, visibleSchema] = await Promise.all([
|
|
4614
|
-
|
|
4615
|
-
|
|
4874
|
+
readFile12(input.targetPath, "utf8"),
|
|
4875
|
+
readFile12(schemaPath, "utf8")
|
|
4616
4876
|
]);
|
|
4617
4877
|
} catch (error) {
|
|
4618
4878
|
return lockedFailure("verify", state, err("WRITE_FAILED", { message: String(error) }));
|
|
@@ -4697,17 +4957,17 @@ function renderPublicationLog(input, added) {
|
|
|
4697
4957
|
}
|
|
4698
4958
|
async function readPageChanged(targetPath, content) {
|
|
4699
4959
|
try {
|
|
4700
|
-
return ok(await
|
|
4960
|
+
return ok(await readFile12(targetPath, "utf8") !== content);
|
|
4701
4961
|
} catch (error) {
|
|
4702
4962
|
if (error.code === "ENOENT") return ok(true);
|
|
4703
4963
|
return err("WRITE_FAILED", { path: targetPath, message: String(error) });
|
|
4704
4964
|
}
|
|
4705
4965
|
}
|
|
4706
4966
|
async function previewPreparedPagePublication(input, vault) {
|
|
4707
|
-
const schemaPath =
|
|
4967
|
+
const schemaPath = join26(vault, "SCHEMA.md");
|
|
4708
4968
|
let schemaText;
|
|
4709
4969
|
try {
|
|
4710
|
-
schemaText = await
|
|
4970
|
+
schemaText = await readFile12(schemaPath, "utf8");
|
|
4711
4971
|
} catch (error) {
|
|
4712
4972
|
const result = err("FILE_NOT_FOUND", { path: schemaPath, message: String(error) });
|
|
4713
4973
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -4719,10 +4979,10 @@ async function previewPreparedPagePublication(input, vault) {
|
|
|
4719
4979
|
if (!reconciled.ok) return { exitCode: errorExitCode(reconciled.error), result: reconciled };
|
|
4720
4980
|
const pageChanged = await readPageChanged(input.targetPath, input.page.content);
|
|
4721
4981
|
if (!pageChanged.ok) return { exitCode: errorExitCode(pageChanged.error), result: pageChanged };
|
|
4722
|
-
const indexPath =
|
|
4982
|
+
const indexPath = join26(vault, "index.md");
|
|
4723
4983
|
let indexText;
|
|
4724
4984
|
try {
|
|
4725
|
-
indexText = await
|
|
4985
|
+
indexText = await readFile12(indexPath, "utf8");
|
|
4726
4986
|
} catch (error) {
|
|
4727
4987
|
const result = err("FILE_NOT_FOUND", { path: indexPath, message: String(error) });
|
|
4728
4988
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -4733,10 +4993,10 @@ async function previewPreparedPagePublication(input, vault) {
|
|
|
4733
4993
|
type: input.page.type
|
|
4734
4994
|
});
|
|
4735
4995
|
if (!index.ok) return { exitCode: errorExitCode(index.error), result: index };
|
|
4736
|
-
const logPath =
|
|
4996
|
+
const logPath = join26(vault, "log.md");
|
|
4737
4997
|
let logText;
|
|
4738
4998
|
try {
|
|
4739
|
-
logText = await
|
|
4999
|
+
logText = await readFile12(logPath, "utf8");
|
|
4740
5000
|
} catch (error) {
|
|
4741
5001
|
const result = err("FILE_NOT_FOUND", { path: logPath, message: String(error) });
|
|
4742
5002
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -4998,7 +5258,7 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
|
|
|
4998
5258
|
}
|
|
4999
5259
|
async function resolveRawCapture(input) {
|
|
5000
5260
|
try {
|
|
5001
|
-
const existing = await
|
|
5261
|
+
const existing = await readFile13(input.path, "utf8");
|
|
5002
5262
|
const frontmatter = extractFrontmatter(existing);
|
|
5003
5263
|
if (!frontmatter.ok) {
|
|
5004
5264
|
return err("INGEST_VALIDATION_FAILED", {
|
|
@@ -5137,7 +5397,7 @@ async function runIngest(input) {
|
|
|
5137
5397
|
sourceContent = fetchResult.data.body;
|
|
5138
5398
|
} else {
|
|
5139
5399
|
try {
|
|
5140
|
-
sourceContent = await
|
|
5400
|
+
sourceContent = await readFile13(input.source, "utf8");
|
|
5141
5401
|
} catch {
|
|
5142
5402
|
return {
|
|
5143
5403
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -5162,7 +5422,7 @@ async function runIngest(input) {
|
|
|
5162
5422
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
5163
5423
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
5164
5424
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
5165
|
-
const rawAbsPath =
|
|
5425
|
+
const rawAbsPath = join27(input.vault, rawRelPath);
|
|
5166
5426
|
const identity = assessSourceIdentity({
|
|
5167
5427
|
rawPath: rawRelPath,
|
|
5168
5428
|
sourceUrl: sourceUrl ?? void 0,
|
|
@@ -5206,11 +5466,11 @@ async function runIngest(input) {
|
|
|
5206
5466
|
);
|
|
5207
5467
|
if (!input.dryRun) {
|
|
5208
5468
|
try {
|
|
5209
|
-
await mkdir9(
|
|
5469
|
+
await mkdir9(join27(input.vault, typedDir), { recursive: true });
|
|
5210
5470
|
} catch (error) {
|
|
5211
5471
|
return {
|
|
5212
5472
|
exitCode: ExitCode.WRITE_FAILED,
|
|
5213
|
-
result: err("WRITE_FAILED", { path:
|
|
5473
|
+
result: err("WRITE_FAILED", { path: join27(input.vault, typedDir), message: String(error) })
|
|
5214
5474
|
};
|
|
5215
5475
|
}
|
|
5216
5476
|
}
|
|
@@ -5257,11 +5517,11 @@ async function runIngest(input) {
|
|
|
5257
5517
|
};
|
|
5258
5518
|
}
|
|
5259
5519
|
try {
|
|
5260
|
-
await mkdir9(
|
|
5520
|
+
await mkdir9(join27(input.vault, "raw", "articles"), { recursive: true });
|
|
5261
5521
|
} catch (error) {
|
|
5262
5522
|
return {
|
|
5263
5523
|
exitCode: ExitCode.WRITE_FAILED,
|
|
5264
|
-
result: err("WRITE_FAILED", { path:
|
|
5524
|
+
result: err("WRITE_FAILED", { path: join27(input.vault, "raw", "articles"), message: String(error) })
|
|
5265
5525
|
};
|
|
5266
5526
|
}
|
|
5267
5527
|
const rawWrite = await writeResolvedRaw({
|
|
@@ -5489,8 +5749,8 @@ ${body}`;
|
|
|
5489
5749
|
}
|
|
5490
5750
|
|
|
5491
5751
|
// src/commands/tag-reconcile.ts
|
|
5492
|
-
import { readFile as
|
|
5493
|
-
import { join as
|
|
5752
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
5753
|
+
import { join as join28, posix } from "path";
|
|
5494
5754
|
var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
|
|
5495
5755
|
function errorExitCode2(error) {
|
|
5496
5756
|
switch (error) {
|
|
@@ -5533,7 +5793,7 @@ function asTagArray(frontmatter, path) {
|
|
|
5533
5793
|
async function readTagsFromFile(path) {
|
|
5534
5794
|
let text;
|
|
5535
5795
|
try {
|
|
5536
|
-
text = await
|
|
5796
|
+
text = await readFile14(path, "utf8");
|
|
5537
5797
|
} catch (error) {
|
|
5538
5798
|
if (error.code === "ENOENT") {
|
|
5539
5799
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path }) };
|
|
@@ -5554,7 +5814,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
5554
5814
|
result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
|
|
5555
5815
|
};
|
|
5556
5816
|
}
|
|
5557
|
-
const source = input.from ?? (explicit.length === 0 ?
|
|
5817
|
+
const source = input.from ?? (explicit.length === 0 ? join28(input.vault, page) : void 0);
|
|
5558
5818
|
if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
|
|
5559
5819
|
const sourced = await readTagsFromFile(source);
|
|
5560
5820
|
if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
|
|
@@ -5565,7 +5825,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
5565
5825
|
}
|
|
5566
5826
|
async function readSchema(schemaPath) {
|
|
5567
5827
|
try {
|
|
5568
|
-
return { exitCode: ExitCode.OK, result: ok(await
|
|
5828
|
+
return { exitCode: ExitCode.OK, result: ok(await readFile14(schemaPath, "utf8")) };
|
|
5569
5829
|
} catch (error) {
|
|
5570
5830
|
if (error.code === "ENOENT") {
|
|
5571
5831
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: schemaPath }) };
|
|
@@ -5593,7 +5853,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
|
|
|
5593
5853
|
};
|
|
5594
5854
|
}
|
|
5595
5855
|
async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
5596
|
-
const schemaPath =
|
|
5856
|
+
const schemaPath = join28(input.vault, "SCHEMA.md");
|
|
5597
5857
|
const current = await readSchema(schemaPath);
|
|
5598
5858
|
if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
|
|
5599
5859
|
const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
|
|
@@ -5604,7 +5864,7 @@ async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
|
5604
5864
|
}
|
|
5605
5865
|
let verifiedText;
|
|
5606
5866
|
try {
|
|
5607
|
-
verifiedText = await
|
|
5867
|
+
verifiedText = await readFile14(schemaPath, "utf8");
|
|
5608
5868
|
} catch (error) {
|
|
5609
5869
|
return {
|
|
5610
5870
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -5630,7 +5890,7 @@ async function runTagReconcile(input) {
|
|
|
5630
5890
|
const comment = taxonomyCommentForPage(page.data, date, input.reason);
|
|
5631
5891
|
if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
|
|
5632
5892
|
if (!input.write) {
|
|
5633
|
-
const schema = await readSchema(
|
|
5893
|
+
const schema = await readSchema(join28(input.vault, "SCHEMA.md"));
|
|
5634
5894
|
if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
|
|
5635
5895
|
const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
|
|
5636
5896
|
return previewResult(page.data, tags, preview, true, []);
|
|
@@ -5683,7 +5943,7 @@ async function runTagReconcile(input) {
|
|
|
5683
5943
|
|
|
5684
5944
|
// src/commands/sync.ts
|
|
5685
5945
|
import { existsSync as existsSync8 } from "fs";
|
|
5686
|
-
import { join as
|
|
5946
|
+
import { join as join29 } from "path";
|
|
5687
5947
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
5688
5948
|
|
|
5689
5949
|
// src/utils/vault-git-pathspec.ts
|
|
@@ -5737,7 +5997,7 @@ function refHasPath(vault, ref, path) {
|
|
|
5737
5997
|
function runSyncStatus(input) {
|
|
5738
5998
|
const vault = input.vault;
|
|
5739
5999
|
const includeStashes = input.includeStashes ?? false;
|
|
5740
|
-
if (!existsSync8(
|
|
6000
|
+
if (!existsSync8(join29(vault, ".git"))) {
|
|
5741
6001
|
return {
|
|
5742
6002
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
5743
6003
|
result: ok({
|
|
@@ -5844,7 +6104,7 @@ function runSyncStatus(input) {
|
|
|
5844
6104
|
}
|
|
5845
6105
|
async function runSyncPush(input) {
|
|
5846
6106
|
const vault = input.vault;
|
|
5847
|
-
if (!existsSync8(
|
|
6107
|
+
if (!existsSync8(join29(vault, ".git"))) {
|
|
5848
6108
|
return {
|
|
5849
6109
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
5850
6110
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -6004,7 +6264,7 @@ function enableGitLongPathsOnWindows(vault) {
|
|
|
6004
6264
|
}
|
|
6005
6265
|
async function runSyncPull(input) {
|
|
6006
6266
|
const vault = input.vault;
|
|
6007
|
-
if (!existsSync8(
|
|
6267
|
+
if (!existsSync8(join29(vault, ".git"))) {
|
|
6008
6268
|
return {
|
|
6009
6269
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6010
6270
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -6232,7 +6492,7 @@ function runSyncJournalClearStale(input) {
|
|
|
6232
6492
|
|
|
6233
6493
|
// src/commands/backup.ts
|
|
6234
6494
|
import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6235
|
-
import { join as
|
|
6495
|
+
import { join as join30, relative as relative2, dirname as dirname6 } from "path";
|
|
6236
6496
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
6237
6497
|
|
|
6238
6498
|
// src/utils/s3-client.ts
|
|
@@ -6256,7 +6516,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
6256
6516
|
function* walkMarkdown(dir, base) {
|
|
6257
6517
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
6258
6518
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
6259
|
-
const full =
|
|
6519
|
+
const full = join30(dir, entry.name);
|
|
6260
6520
|
if (entry.isDirectory()) {
|
|
6261
6521
|
yield* walkMarkdown(full, base);
|
|
6262
6522
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -6279,7 +6539,7 @@ async function runBackupSync(input) {
|
|
|
6279
6539
|
let failed = 0;
|
|
6280
6540
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
6281
6541
|
for (const relPath of files) {
|
|
6282
|
-
const absPath =
|
|
6542
|
+
const absPath = join30(input.vault, relPath);
|
|
6283
6543
|
const localStat = statSync2(absPath);
|
|
6284
6544
|
let needsUpload = true;
|
|
6285
6545
|
try {
|
|
@@ -6355,7 +6615,7 @@ async function runBackupRestore(input) {
|
|
|
6355
6615
|
const objects = list.Contents ?? [];
|
|
6356
6616
|
for (const obj of objects) {
|
|
6357
6617
|
if (!obj.Key) continue;
|
|
6358
|
-
const localPath =
|
|
6618
|
+
const localPath = join30(target, obj.Key);
|
|
6359
6619
|
try {
|
|
6360
6620
|
const localStat = statSync2(localPath);
|
|
6361
6621
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -6402,8 +6662,8 @@ async function runBackupRestore(input) {
|
|
|
6402
6662
|
|
|
6403
6663
|
// src/commands/status.ts
|
|
6404
6664
|
import { existsSync as existsSync9, statSync as statSync3 } from "fs";
|
|
6405
|
-
import { readFile as
|
|
6406
|
-
import { join as
|
|
6665
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
6666
|
+
import { join as join31 } from "path";
|
|
6407
6667
|
async function runStatus(input) {
|
|
6408
6668
|
if (!existsSync9(input.vault)) {
|
|
6409
6669
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
@@ -6430,7 +6690,7 @@ async function runStatus(input) {
|
|
|
6430
6690
|
const compound = scan.data.compound.length;
|
|
6431
6691
|
let schemaVersion = "v1";
|
|
6432
6692
|
try {
|
|
6433
|
-
const schemaContent = await
|
|
6693
|
+
const schemaContent = await readFile15(join31(input.vault, "SCHEMA.md"), "utf8");
|
|
6434
6694
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
6435
6695
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
6436
6696
|
} catch {
|
|
@@ -6491,7 +6751,7 @@ async function runStatus(input) {
|
|
|
6491
6751
|
|
|
6492
6752
|
// src/commands/seed.ts
|
|
6493
6753
|
import { mkdir as mkdir10, writeFile as writeFile9, stat as stat4 } from "fs/promises";
|
|
6494
|
-
import { join as
|
|
6754
|
+
import { join as join32 } from "path";
|
|
6495
6755
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6496
6756
|
var EXAMPLE_PAGES = {
|
|
6497
6757
|
"entities/example-project.md": `---
|
|
@@ -6560,29 +6820,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
6560
6820
|
`;
|
|
6561
6821
|
async function runSeed(input) {
|
|
6562
6822
|
try {
|
|
6563
|
-
await stat4(
|
|
6823
|
+
await stat4(join32(input.vault, "SCHEMA.md"));
|
|
6564
6824
|
} catch {
|
|
6565
6825
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
6566
6826
|
}
|
|
6567
6827
|
const created = [];
|
|
6568
6828
|
const skipped = [];
|
|
6569
6829
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
6570
|
-
const absPath =
|
|
6830
|
+
const absPath = join32(input.vault, relPath);
|
|
6571
6831
|
try {
|
|
6572
6832
|
await stat4(absPath);
|
|
6573
6833
|
skipped.push(relPath);
|
|
6574
6834
|
} catch {
|
|
6575
|
-
await mkdir10(
|
|
6835
|
+
await mkdir10(join32(absPath, ".."), { recursive: true });
|
|
6576
6836
|
await writeFile9(absPath, content, "utf8");
|
|
6577
6837
|
created.push(relPath);
|
|
6578
6838
|
}
|
|
6579
6839
|
}
|
|
6580
|
-
const rawPath =
|
|
6840
|
+
const rawPath = join32(input.vault, "raw", "articles", "example-source.md");
|
|
6581
6841
|
try {
|
|
6582
6842
|
await stat4(rawPath);
|
|
6583
6843
|
skipped.push("raw/articles/example-source.md");
|
|
6584
6844
|
} catch {
|
|
6585
|
-
await mkdir10(
|
|
6845
|
+
await mkdir10(join32(rawPath, ".."), { recursive: true });
|
|
6586
6846
|
await writeFile9(rawPath, EXAMPLE_RAW, "utf8");
|
|
6587
6847
|
created.push("raw/articles/example-source.md");
|
|
6588
6848
|
}
|
|
@@ -6605,9 +6865,9 @@ async function runSeed(input) {
|
|
|
6605
6865
|
}
|
|
6606
6866
|
|
|
6607
6867
|
// src/commands/canvas.ts
|
|
6608
|
-
import { readFile as
|
|
6868
|
+
import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
|
|
6609
6869
|
import { existsSync as existsSync10 } from "fs";
|
|
6610
|
-
import { join as
|
|
6870
|
+
import { join as join33 } from "path";
|
|
6611
6871
|
var NODE_WIDTH = 240;
|
|
6612
6872
|
var NODE_HEIGHT = 60;
|
|
6613
6873
|
var COLUMN_SPACING = 400;
|
|
@@ -6685,7 +6945,7 @@ function buildCanvasEdges(adjacency) {
|
|
|
6685
6945
|
return edges;
|
|
6686
6946
|
}
|
|
6687
6947
|
async function runCanvasGenerate(input) {
|
|
6688
|
-
const graphPath = input.graphPath ??
|
|
6948
|
+
const graphPath = input.graphPath ?? join33(input.vault, ".skillwiki", "graph.json");
|
|
6689
6949
|
if (!existsSync10(graphPath)) {
|
|
6690
6950
|
return {
|
|
6691
6951
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6697,7 +6957,7 @@ async function runCanvasGenerate(input) {
|
|
|
6697
6957
|
}
|
|
6698
6958
|
let raw;
|
|
6699
6959
|
try {
|
|
6700
|
-
raw = await
|
|
6960
|
+
raw = await readFile16(graphPath, "utf8");
|
|
6701
6961
|
} catch (e) {
|
|
6702
6962
|
return {
|
|
6703
6963
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6723,7 +6983,7 @@ async function runCanvasGenerate(input) {
|
|
|
6723
6983
|
const nodes = buildCanvasNodes(paths);
|
|
6724
6984
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
6725
6985
|
const canvas = { nodes, edges };
|
|
6726
|
-
const outPath =
|
|
6986
|
+
const outPath = join33(input.vault, "vault-graph.canvas");
|
|
6727
6987
|
try {
|
|
6728
6988
|
await writeFile10(outPath, JSON.stringify(canvas, null, 2));
|
|
6729
6989
|
} catch (e) {
|
|
@@ -6748,12 +7008,12 @@ written: ${outPath}`
|
|
|
6748
7008
|
import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
|
|
6749
7009
|
import { execSync as nodeExecSync } from "child_process";
|
|
6750
7010
|
import { hostname as nodeHostname, platform as nodePlatform } from "os";
|
|
6751
|
-
import { join as
|
|
7011
|
+
import { join as join34 } from "path";
|
|
6752
7012
|
var SSH_TIMEOUT_MS = 15e3;
|
|
6753
7013
|
var TIMER_UNIT = "agent-memory-trends.timer";
|
|
6754
7014
|
var SERVICE_UNIT = "agent-memory-trends.service";
|
|
6755
7015
|
var SYSTEMD_SERVICE_FAILED_CLASS = "SYSTEMD_SERVICE_FAILED";
|
|
6756
|
-
function
|
|
7016
|
+
function defaultDeps3() {
|
|
6757
7017
|
return {
|
|
6758
7018
|
platform: () => nodePlatform(),
|
|
6759
7019
|
execSync: nodeExecSync
|
|
@@ -6973,12 +7233,12 @@ function rowHealthy(reachable, timer, runUnhealthy, timerBad, platform2) {
|
|
|
6973
7233
|
return true;
|
|
6974
7234
|
}
|
|
6975
7235
|
async function runFleetHealth(input) {
|
|
6976
|
-
const deps = input.deps ??
|
|
7236
|
+
const deps = input.deps ?? defaultDeps3();
|
|
6977
7237
|
const env = input.env ?? process.env;
|
|
6978
7238
|
const home = input.home ?? env.HOME ?? "";
|
|
6979
7239
|
const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
|
|
6980
7240
|
const vault = input.vault ?? env.WIKI_PATH;
|
|
6981
|
-
const file = input.file ?? (vault ?
|
|
7241
|
+
const file = input.file ?? (vault ? join34(vault, FLEET_REL_PATH) : void 0);
|
|
6982
7242
|
if (!file) {
|
|
6983
7243
|
return {
|
|
6984
7244
|
exitCode: ExitCode.NO_VAULT_CONFIGURED,
|
|
@@ -7063,14 +7323,14 @@ async function runFleetHealth(input) {
|
|
|
7063
7323
|
|
|
7064
7324
|
// src/utils/auto-commit.ts
|
|
7065
7325
|
import { existsSync as existsSync12 } from "fs";
|
|
7066
|
-
import { join as
|
|
7326
|
+
import { join as join35 } from "path";
|
|
7067
7327
|
async function postCommit(vault, exitCode) {
|
|
7068
7328
|
if (exitCode !== 0) return;
|
|
7069
7329
|
const home = process.env.HOME ?? "";
|
|
7070
7330
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
7071
7331
|
const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
|
|
7072
7332
|
if (autoCommit === "false") return;
|
|
7073
|
-
if (!existsSync12(
|
|
7333
|
+
if (!existsSync12(join35(vault, ".git"))) return;
|
|
7074
7334
|
const lastOps = readLastOp(vault);
|
|
7075
7335
|
if (lastOps.length === 0) return;
|
|
7076
7336
|
const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
|
|
@@ -7249,7 +7509,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
7249
7509
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
7250
7510
|
});
|
|
7251
7511
|
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
7252
|
-
const out = opts.out ??
|
|
7512
|
+
const out = opts.out ?? join36(vault, ".skillwiki", "graph.json");
|
|
7253
7513
|
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
7254
7514
|
});
|
|
7255
7515
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
@@ -7519,6 +7779,33 @@ projectionsCmd.command("materialize [vault]").description("preview or write root
|
|
|
7519
7779
|
);
|
|
7520
7780
|
} else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
|
|
7521
7781
|
});
|
|
7782
|
+
projectionsCmd.command("repair-legacy [vault]").description("repair one supported legacy root-index marker pair and log event").requiredOption("--event-operation-id <id>", "exact legacy event operation ID").option("--write", "write the bounded repairs", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
7783
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
7784
|
+
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
7785
|
+
else if (opts.write) {
|
|
7786
|
+
return emitManagedVaultWrite(
|
|
7787
|
+
v.vault,
|
|
7788
|
+
"projections repair-legacy",
|
|
7789
|
+
(receipt) => runProjectionsRepairLegacy({
|
|
7790
|
+
vault: v.vault,
|
|
7791
|
+
eventOperationId: opts.eventOperationId,
|
|
7792
|
+
write: true,
|
|
7793
|
+
hostId: receipt.host_id
|
|
7794
|
+
}),
|
|
7795
|
+
{ convergenceVault: opts.convergeVault, postCommit: false }
|
|
7796
|
+
);
|
|
7797
|
+
} else {
|
|
7798
|
+
return emit(
|
|
7799
|
+
await runProjectionsRepairLegacy({
|
|
7800
|
+
vault: v.vault,
|
|
7801
|
+
eventOperationId: opts.eventOperationId,
|
|
7802
|
+
write: false
|
|
7803
|
+
}),
|
|
7804
|
+
v.vault,
|
|
7805
|
+
{ postCommit: false }
|
|
7806
|
+
);
|
|
7807
|
+
}
|
|
7808
|
+
});
|
|
7522
7809
|
program.command("lint [vault]").description("run all vault health checks").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--fix", "auto-fix supported lint violations").option("--only <bucket>", "run only the specified lint bucket").option("--summary", "emit bounded bucket counts instead of full item arrays", false).option("--examples <n>", "example count per bucket in summary mode", (s) => parseInt(s, 10), 3).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
7523
7810
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
7524
7811
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|