silvermoon 0.0.1

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/src/index.js ADDED
@@ -0,0 +1,260 @@
1
+ import { resolve } from "node:path";
2
+
3
+ import { loadConfig } from "./config.js";
4
+ import {
5
+ fetchPrimary,
6
+ indexSnapshot,
7
+ resolveCommit,
8
+ runGit,
9
+ worktreeSnapshot,
10
+ withTemporaryTree,
11
+ withTemporaryWorktree,
12
+ } from "./git.js";
13
+ import { inspectIdeaLayout } from "./idea-layout.js";
14
+
15
+ function ideaSummary(idea) {
16
+ const value = {
17
+ id: idea.id,
18
+ idealRevision: idea.idealRevision,
19
+ implementationRevision: idea.implementationRevision,
20
+ deploymentRevision: idea.deploymentRevision,
21
+ state: idea.state,
22
+ };
23
+ if (idea.alias !== undefined) value.alias = idea.alias;
24
+ return value;
25
+ }
26
+
27
+ async function inspectSnapshot({
28
+ baseRevision,
29
+ gitRoot = root,
30
+ historyCommit,
31
+ root,
32
+ snapshotTree,
33
+ target = "local",
34
+ validateCandidate = false,
35
+ }) {
36
+ const repositoryRoot = resolve(root);
37
+ const loaded = await loadConfig({ root: repositoryRoot });
38
+ const layout = loaded.config
39
+ ? await inspectIdeaLayout({
40
+ baseRevision,
41
+ config: loaded.config,
42
+ gitRoot,
43
+ historyCommit,
44
+ root: repositoryRoot,
45
+ snapshotTree,
46
+ validateCandidate,
47
+ })
48
+ : { diagnostics: [], ideas: [] };
49
+ const diagnostics = [
50
+ ...loaded.diagnostics,
51
+ ...layout.diagnostics,
52
+ ];
53
+
54
+ const report = {
55
+ command: "check",
56
+ ok: diagnostics.every(({ level }) => level !== "error"),
57
+ root: repositoryRoot,
58
+ diagnostics,
59
+ result: {
60
+ target,
61
+ checked: layout.ideas.length,
62
+ ideas: layout.ideas.map(ideaSummary),
63
+ },
64
+ };
65
+ return { config: loaded.config, layout, report };
66
+ }
67
+
68
+ function targetFailure(root, code, message, remediation) {
69
+ return {
70
+ command: "check",
71
+ ok: false,
72
+ root,
73
+ diagnostics: [{ code, level: "error", message, remediation }],
74
+ result: null,
75
+ };
76
+ }
77
+
78
+ function finishTarget({ commit, inspected, root, target }) {
79
+ const report = inspected.report;
80
+ report.root = root;
81
+ report.result = { ...report.result, target };
82
+ if (commit) report.result.commit = commit;
83
+ report.ok = report.diagnostics.every(({ level }) => level !== "error");
84
+ return report;
85
+ }
86
+
87
+ export async function checkRepository({
88
+ commit,
89
+ remote = false,
90
+ root = process.cwd(),
91
+ staged = false,
92
+ worktree = false,
93
+ } = {}) {
94
+ const repositoryRoot = resolve(root);
95
+ const targetCount = [remote, commit !== undefined, staged, worktree].filter(Boolean).length;
96
+ if (targetCount > 1) {
97
+ return targetFailure(
98
+ repositoryRoot,
99
+ "check.target.conflict",
100
+ "Check targets are mutually exclusive.",
101
+ "Choose exactly one of remote, commit, staged, or worktree.",
102
+ );
103
+ }
104
+
105
+ if (remote) {
106
+ try {
107
+ const head = resolveCommit(repositoryRoot, "HEAD");
108
+ const bootstrap = await withTemporaryWorktree(repositoryRoot, head, (worktreeRoot, tree) =>
109
+ inspectSnapshot({
110
+ gitRoot: repositoryRoot,
111
+ root: worktreeRoot,
112
+ snapshotTree: tree,
113
+ target: "remote",
114
+ }),
115
+ );
116
+ if (!bootstrap.config) {
117
+ return finishTarget({ inspected: bootstrap, root: repositoryRoot, target: "remote" });
118
+ }
119
+ const primary = fetchPrimary(repositoryRoot, bootstrap.config);
120
+ const inspected = await withTemporaryWorktree(repositoryRoot, primary, (worktree, tree) =>
121
+ inspectSnapshot({
122
+ gitRoot: repositoryRoot,
123
+ historyCommit: primary,
124
+ root: worktree,
125
+ snapshotTree: tree,
126
+ target: "remote",
127
+ }),
128
+ );
129
+ return finishTarget({
130
+ commit: primary,
131
+ inspected,
132
+ root: repositoryRoot,
133
+ target: "remote",
134
+ });
135
+ } catch (caught) {
136
+ return targetFailure(
137
+ repositoryRoot,
138
+ "git.fetch.failed",
139
+ caught.message,
140
+ "Check remote access and retry.",
141
+ );
142
+ }
143
+ }
144
+
145
+ if (commit !== undefined || (!staged && !worktree)) {
146
+ const revision = commit ?? "HEAD";
147
+ const target = commit === undefined ? "head" : "commit";
148
+ let resolvedCommit;
149
+ try {
150
+ resolvedCommit = resolveCommit(repositoryRoot, revision);
151
+ } catch (caught) {
152
+ return targetFailure(
153
+ repositoryRoot,
154
+ "git.commit.invalid",
155
+ caught.message,
156
+ commit === undefined
157
+ ? "Create or restore a valid HEAD commit."
158
+ : "Choose a commit available in the local repository.",
159
+ );
160
+ }
161
+ try {
162
+ const parent = runGit(repositoryRoot, ["rev-parse", `${resolvedCommit}^1`]);
163
+ const inspected = await withTemporaryWorktree(repositoryRoot, resolvedCommit, (worktree, tree) =>
164
+ inspectSnapshot({
165
+ baseRevision: parent.ok ? parent.stdout : null,
166
+ gitRoot: repositoryRoot,
167
+ root: worktree,
168
+ snapshotTree: tree,
169
+ target,
170
+ validateCandidate: true,
171
+ }),
172
+ );
173
+ return finishTarget({
174
+ commit: resolvedCommit,
175
+ inspected,
176
+ root: repositoryRoot,
177
+ target,
178
+ });
179
+ } catch (caught) {
180
+ return targetFailure(
181
+ repositoryRoot,
182
+ "git.snapshot.failed",
183
+ caught.message,
184
+ "Resolve the repository state and retry the commit check.",
185
+ );
186
+ }
187
+ }
188
+
189
+ if (staged) {
190
+ try {
191
+ const snapshot = indexSnapshot(repositoryRoot);
192
+ const inspected = await withTemporaryTree(repositoryRoot, snapshot.tree, (worktree) =>
193
+ inspectSnapshot({
194
+ baseRevision: "HEAD",
195
+ gitRoot: repositoryRoot,
196
+ root: worktree,
197
+ snapshotTree: snapshot.tree,
198
+ target: "staged",
199
+ validateCandidate: true,
200
+ }),
201
+ );
202
+ return finishTarget({
203
+ inspected,
204
+ root: repositoryRoot,
205
+ target: "staged",
206
+ });
207
+ } catch (caught) {
208
+ return targetFailure(
209
+ repositoryRoot,
210
+ "git.snapshot.failed",
211
+ caught.message,
212
+ "Resolve the index state and retry the staged check.",
213
+ );
214
+ }
215
+ }
216
+
217
+ if (worktree) {
218
+ try {
219
+ const snapshot = worktreeSnapshot(repositoryRoot);
220
+ const inspected = await withTemporaryTree(repositoryRoot, snapshot.tree, (worktree) =>
221
+ inspectSnapshot({
222
+ baseRevision: "HEAD",
223
+ gitRoot: repositoryRoot,
224
+ root: worktree,
225
+ snapshotTree: snapshot.tree,
226
+ target: "worktree",
227
+ validateCandidate: true,
228
+ }),
229
+ );
230
+ return finishTarget({
231
+ inspected,
232
+ root: repositoryRoot,
233
+ target: "worktree",
234
+ });
235
+ } catch (caught) {
236
+ return targetFailure(
237
+ repositoryRoot,
238
+ "git.snapshot.failed",
239
+ caught.message,
240
+ "Resolve the worktree state and retry the worktree check.",
241
+ );
242
+ }
243
+ }
244
+
245
+ throw new Error("Unreachable check target");
246
+ }
247
+
248
+ export {
249
+ deriveIdeaState,
250
+ isValidUlid,
251
+ parseIdeaStatus,
252
+ serializeIdeaStatus,
253
+ validateIdeaStatus,
254
+ } from "./ideas.js";
255
+ export { createIdea, generateUlid } from "./create-idea.js";
256
+ export {
257
+ inspectAdoption,
258
+ SILVERMOON_VERSION,
259
+ } from "./adoption.js";
260
+ export { whatsNext } from "./whatsnext.js";
package/src/layout.js ADDED
@@ -0,0 +1,21 @@
1
+ export const METADATA_ROOT = ".silvermoon";
2
+ export const CONFIG_PATH = `${METADATA_ROOT}/config.yaml`;
3
+ export const IDEAS_ROOT = `${METADATA_ROOT}/ideas`;
4
+
5
+ export function ideaPaths(id) {
6
+ const ideaPath = `${IDEAS_ROOT}/${id}`;
7
+ const outerPath = `${ideaPath}/outer`;
8
+ const innerPath = `${outerPath}/inner`;
9
+ const idealPath = `${innerPath}/ideal`;
10
+ return {
11
+ ideaPath,
12
+ statusPath: `${ideaPath}/status.yaml`,
13
+ ledgerPath: `${ideaPath}/ledger.md`,
14
+ outerPath,
15
+ deploymentDocumentPath: `${outerPath}/Deployment.md`,
16
+ innerPath,
17
+ implementationDocumentPath: `${innerPath}/Implementation.md`,
18
+ idealPath,
19
+ ideaDocumentPath: `${idealPath}/Idea.md`,
20
+ };
21
+ }
@@ -0,0 +1,88 @@
1
+ import { createHash } from "node:crypto";
2
+ import { spawnSync } from "node:child_process";
3
+
4
+ const CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/;
5
+ const ENCODED_SEPARATOR = /%(?:2f|5c)/i;
6
+ const CANONICAL_HOST = /^(?:[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?|\[[0-9a-f:.]+\])$/;
7
+ const INVALID_BRANCH_CHARACTER = /[\u0000-\u0020\u007f~^:?*\[\\]/;
8
+
9
+ export function canonicalRepository(value) {
10
+ if (
11
+ typeof value !== "string" ||
12
+ value.length === 0 ||
13
+ CONTROL_CHARACTER.test(value) ||
14
+ value.includes("\\")
15
+ ) {
16
+ return null;
17
+ }
18
+
19
+ let parsed;
20
+ try {
21
+ parsed = new URL(value);
22
+ } catch {
23
+ return null;
24
+ }
25
+
26
+ if (
27
+ parsed.protocol !== "https:" ||
28
+ !parsed.hostname ||
29
+ !CANONICAL_HOST.test(parsed.hostname) ||
30
+ parsed.username ||
31
+ parsed.password ||
32
+ parsed.search ||
33
+ parsed.hash ||
34
+ parsed.pathname === "/" ||
35
+ parsed.pathname.endsWith("/") ||
36
+ parsed.pathname.includes("//") ||
37
+ ENCODED_SEPARATOR.test(parsed.pathname) ||
38
+ parsed.href !== value
39
+ ) {
40
+ return null;
41
+ }
42
+
43
+ return parsed.href;
44
+ }
45
+
46
+ export function validRepository(value) {
47
+ return canonicalRepository(value) !== null;
48
+ }
49
+
50
+ export function repositoryNamespace(value) {
51
+ const canonical = canonicalRepository(value);
52
+ if (!canonical) throw new Error(`Invalid repository URL: ${String(value)}`);
53
+ return createHash("sha256").update(canonical).digest("hex");
54
+ }
55
+
56
+ export function validBranchName(value) {
57
+ if (
58
+ typeof value !== "string" ||
59
+ value.length === 0 ||
60
+ value === "@" ||
61
+ value.startsWith("-") ||
62
+ value.startsWith("refs/") ||
63
+ value.startsWith("remotes/") ||
64
+ value.endsWith("/") ||
65
+ value.endsWith(".") ||
66
+ value.includes("//") ||
67
+ value.includes("..") ||
68
+ value.includes("@{") ||
69
+ INVALID_BRANCH_CHARACTER.test(value)
70
+ ) {
71
+ return false;
72
+ }
73
+
74
+ const structurallyValid = value
75
+ .split("/")
76
+ .every((component) => component && !component.startsWith(".") && !component.endsWith(".lock"));
77
+ if (!structurallyValid) return false;
78
+
79
+ const checked = spawnSync("git", ["check-ref-format", "--branch", value], {
80
+ encoding: "utf8",
81
+ windowsHide: true,
82
+ });
83
+ return checked.status === 0;
84
+ }
85
+
86
+ export function effectiveSourceRepository(config, record) {
87
+ return record.sourceRepository ?? config.primaryRepository;
88
+ }