@stage5/lumine 0.2.15 → 0.2.16
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/README.md +5 -0
- package/lib/api.js +15 -0
- package/lib/commands.js +65 -1
- package/lib/constants.js +2 -0
- package/lib/workspace.js +17 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ npx @stage5/lumine@latest new --title "Daily Reflection App" --description "Priv
|
|
|
10
10
|
npx @stage5/lumine@latest rename "My New Build Title"
|
|
11
11
|
npx @stage5/lumine@latest rename "New Title" --target 123
|
|
12
12
|
npx @stage5/lumine@latest projects
|
|
13
|
+
npx @stage5/lumine@latest branches 884
|
|
13
14
|
npx @stage5/lumine@latest explore --sort forks
|
|
14
15
|
npx @stage5/lumine@latest reference https://www.twin-kle.com/app/123
|
|
15
16
|
npx @stage5/lumine@latest fork https://www.twin-kle.com/app/123
|
|
@@ -35,6 +36,10 @@ pulling the owner's main project creates or reuses your contribution branch and
|
|
|
35
36
|
checks out that branch locally. Saves go to your branch, so the project owner
|
|
36
37
|
can merge or replace main from Twinkle.
|
|
37
38
|
|
|
39
|
+
Use `lumine branches <build-url-or-id>` to list the contribution branches you
|
|
40
|
+
can review, including each contributor, branch number, status, and URL. Then use
|
|
41
|
+
`lumine diff <branch-url>` to inspect one branch.
|
|
42
|
+
|
|
38
43
|
Use `lumine explore` to list public open-source Build apps that can be used as
|
|
39
44
|
examples or starting points. It supports `--search` and `--sort forks`,
|
|
40
45
|
`--sort popular`, or `--sort recent`. Use `lumine reference <build-url-or-id>`
|
package/lib/api.js
CHANGED
|
@@ -30,6 +30,21 @@ export async function listOpenSourceBuilds({ options, auth }) {
|
|
|
30
30
|
return Array.isArray(result.builds) ? result.builds : [];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export async function listContributionBranches({
|
|
34
|
+
options,
|
|
35
|
+
auth,
|
|
36
|
+
buildId,
|
|
37
|
+
limit,
|
|
38
|
+
}) {
|
|
39
|
+
const url = new URL(`${options.apiUrl}/build/${buildId}/contributions`);
|
|
40
|
+
url.searchParams.set("limit", String(limit));
|
|
41
|
+
return await requestJson({
|
|
42
|
+
url: url.toString(),
|
|
43
|
+
authToken: auth.token,
|
|
44
|
+
timeoutMs: options.timeoutMs,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
export async function loadBuildMetadata({ options, auth, buildId }) {
|
|
34
49
|
const result = await loadBuildFiles({
|
|
35
50
|
options,
|
package/lib/commands.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
loadBuildVersionFiles,
|
|
32
32
|
loadBuildVersions,
|
|
33
33
|
loadContributionDiff,
|
|
34
|
+
listContributionBranches,
|
|
34
35
|
loadLumineCliVersionInfo,
|
|
35
36
|
loadOpenSourceBuildFiles,
|
|
36
37
|
maybeCheckForLumineCliUpdate,
|
|
@@ -76,6 +77,7 @@ import {
|
|
|
76
77
|
collectProjectLimitFindings,
|
|
77
78
|
findLocalProjectMetadata,
|
|
78
79
|
isIndexHtmlPath,
|
|
80
|
+
isReadOnlyProjectMetadata,
|
|
79
81
|
isReadOnlyReferenceMetadata,
|
|
80
82
|
removeLocalProjectFilesNotIn,
|
|
81
83
|
stashLocalProjectFilesBeforePull,
|
|
@@ -130,6 +132,10 @@ export async function main() {
|
|
|
130
132
|
await projects(options);
|
|
131
133
|
return;
|
|
132
134
|
}
|
|
135
|
+
if (options.command === "branches") {
|
|
136
|
+
await branches(options);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
133
139
|
if (options.command === "explore") {
|
|
134
140
|
await explore(options);
|
|
135
141
|
return;
|
|
@@ -263,7 +269,8 @@ export async function renameBuild(options) {
|
|
|
263
269
|
await saveSelectedBuild({ options, auth, build: updatedBuild });
|
|
264
270
|
if (
|
|
265
271
|
localProject?.rootDir &&
|
|
266
|
-
Number(localProject.metadata?.buildId || 0) === buildId
|
|
272
|
+
Number(localProject.metadata?.buildId || 0) === buildId &&
|
|
273
|
+
!isReadOnlyProjectMetadata(localProject.metadata)
|
|
267
274
|
) {
|
|
268
275
|
await writeProjectMetadata({
|
|
269
276
|
dir: localProject.rootDir,
|
|
@@ -303,6 +310,33 @@ export async function projects(options) {
|
|
|
303
310
|
printBuildList(builds);
|
|
304
311
|
}
|
|
305
312
|
|
|
313
|
+
export async function branches(options) {
|
|
314
|
+
const auth = await resolveAuth(options);
|
|
315
|
+
const requestedBuildId = await resolveRequiredBuildIdOrSelected(
|
|
316
|
+
options,
|
|
317
|
+
auth,
|
|
318
|
+
);
|
|
319
|
+
const requestedBuild = await loadBuildMetadata({
|
|
320
|
+
options,
|
|
321
|
+
auth,
|
|
322
|
+
buildId: requestedBuildId,
|
|
323
|
+
});
|
|
324
|
+
const rootBuildId =
|
|
325
|
+
Number(requestedBuild.contributionRootBuildId || 0) ||
|
|
326
|
+
Number(requestedBuild.id || 0);
|
|
327
|
+
const rootBuild =
|
|
328
|
+
rootBuildId === Number(requestedBuild.id || 0)
|
|
329
|
+
? requestedBuild
|
|
330
|
+
: await loadBuildMetadata({ options, auth, buildId: rootBuildId });
|
|
331
|
+
const result = await listContributionBranches({
|
|
332
|
+
options,
|
|
333
|
+
auth,
|
|
334
|
+
buildId: rootBuildId,
|
|
335
|
+
limit: options.limit,
|
|
336
|
+
});
|
|
337
|
+
printContributionBranches({ result, rootBuild, options });
|
|
338
|
+
}
|
|
339
|
+
|
|
306
340
|
export async function explore(options) {
|
|
307
341
|
const auth = await resolveAuth(options);
|
|
308
342
|
const builds = await listOpenSourceBuilds({ options, auth });
|
|
@@ -1618,6 +1652,34 @@ export function printContributionDiff({ result, build }) {
|
|
|
1618
1652
|
}
|
|
1619
1653
|
}
|
|
1620
1654
|
|
|
1655
|
+
export function printContributionBranches({ result, rootBuild, options }) {
|
|
1656
|
+
const contributions = Array.isArray(result?.contributions)
|
|
1657
|
+
? result.contributions
|
|
1658
|
+
: [];
|
|
1659
|
+
console.log(`Branches for ${formatBuildTitle(rootBuild)}:`);
|
|
1660
|
+
if (!contributions.length) {
|
|
1661
|
+
console.log("No contribution branches.");
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
for (const [index, contribution] of contributions.entries()) {
|
|
1665
|
+
const branchNumber = Number(contribution.contributionBranchNumber || 0);
|
|
1666
|
+
const branchLabel = branchNumber
|
|
1667
|
+
? `branch ${branchNumber}`
|
|
1668
|
+
: `branch #${contribution.id}`;
|
|
1669
|
+
const title = String(contribution.title || "Untitled branch").trim();
|
|
1670
|
+
const contributor = String(contribution.username || "unknown").trim();
|
|
1671
|
+
const status = String(contribution.contributionStatus || "draft").trim();
|
|
1672
|
+
console.log(
|
|
1673
|
+
`${index + 1}. ${title} - ${contributor} - ${branchLabel} (${status})`,
|
|
1674
|
+
);
|
|
1675
|
+
if (branchNumber) {
|
|
1676
|
+
console.log(
|
|
1677
|
+
` ${options.siteUrl}/build/${Number(rootBuild.id)}/${branchNumber}`,
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1621
1683
|
export function printUpdateFromMainResult({ result, build, dir, mergedFiles }) {
|
|
1622
1684
|
const branchNumber = Number(build?.contributionBranchNumber || 0) || 0;
|
|
1623
1685
|
const branchLabel = branchNumber
|
|
@@ -1981,6 +2043,7 @@ export function printHelp() {
|
|
|
1981
2043
|
lumine new [title]
|
|
1982
2044
|
lumine rename [title] [--target <twinkle-build-url-or-id>]
|
|
1983
2045
|
lumine projects
|
|
2046
|
+
lumine branches [twinkle-build-url-or-id] [--limit <n>]
|
|
1984
2047
|
lumine explore [search terms]
|
|
1985
2048
|
lumine select [twinkle-build-url]
|
|
1986
2049
|
lumine pull [twinkle-build-url]
|
|
@@ -2016,6 +2079,7 @@ Examples:
|
|
|
2016
2079
|
npx @stage5/lumine@latest rename "My New Build Title"
|
|
2017
2080
|
npx @stage5/lumine@latest rename "New Title" --target 123
|
|
2018
2081
|
npx @stage5/lumine@latest new --title "Daily Reflection App" --description "Private journal with streaks"
|
|
2082
|
+
npx @stage5/lumine@latest branches 884
|
|
2019
2083
|
npx @stage5/lumine@latest explore --sort forks
|
|
2020
2084
|
npx @stage5/lumine@latest reference https://www.twin-kle.com/app/123
|
|
2021
2085
|
npx @stage5/lumine@latest fork https://www.twin-kle.com/app/123
|
package/lib/constants.js
CHANGED
|
@@ -308,6 +308,7 @@ export const AGENT_INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
|
308
308
|
export const MAIN_CHECKOUT_READONLY_COMMANDS = new Set([
|
|
309
309
|
"pull",
|
|
310
310
|
"check",
|
|
311
|
+
"branches",
|
|
311
312
|
"diff",
|
|
312
313
|
"sdk",
|
|
313
314
|
"select",
|
|
@@ -326,6 +327,7 @@ export const COMMANDS = new Set([
|
|
|
326
327
|
"new",
|
|
327
328
|
"rename",
|
|
328
329
|
"projects",
|
|
330
|
+
"branches",
|
|
329
331
|
"explore",
|
|
330
332
|
"select",
|
|
331
333
|
"pull",
|
package/lib/workspace.js
CHANGED
|
@@ -779,14 +779,15 @@ export function resolveProjectDirForSave({ options, localProject }) {
|
|
|
779
779
|
export function assertLocalProjectCanBeSaved(localProject) {
|
|
780
780
|
const metadata = localProject?.metadata;
|
|
781
781
|
if (!metadata) return;
|
|
782
|
-
|
|
782
|
+
const readOnlyKind = readOnlyProjectMetadataKind(metadata);
|
|
783
|
+
if (readOnlyKind === "main") {
|
|
783
784
|
const rootBuildId =
|
|
784
785
|
Number(metadata.buildId || 0) || Number(metadata.build?.id || 0) || 0;
|
|
785
786
|
throw new Error(
|
|
786
787
|
`This is a read-only checkout of main${rootBuildId ? ` for Build ${rootBuildId}` : ""}. Make edits in your branch workspace (\`lumine pull${rootBuildId ? ` ${rootBuildId}` : ""}\`), and run \`lumine update-from-main\` there to bring main's changes into it.`,
|
|
787
788
|
);
|
|
788
789
|
}
|
|
789
|
-
if (
|
|
790
|
+
if (readOnlyKind === "version") {
|
|
790
791
|
const checkoutBuildId =
|
|
791
792
|
Number(metadata.buildId || 0) || Number(metadata.build?.id || 0) || 0;
|
|
792
793
|
const checkoutVersion = Number(metadata.checkoutVersion || 0) || 0;
|
|
@@ -794,7 +795,7 @@ export function assertLocalProjectCanBeSaved(localProject) {
|
|
|
794
795
|
`This is a read-only checkout of a previous save${checkoutVersion ? ` (v${checkoutVersion})` : ""}${checkoutBuildId ? ` for Build ${checkoutBuildId}` : ""}. To bring this save back, run \`lumine restore${checkoutVersion ? ` ${checkoutVersion}` : " <n>"}\` from the editable workspace, then \`lumine save\`.`,
|
|
795
796
|
);
|
|
796
797
|
}
|
|
797
|
-
if (
|
|
798
|
+
if (readOnlyKind === "reference") {
|
|
798
799
|
const sourceBuildId =
|
|
799
800
|
Number(metadata.reference?.sourceBuildId || 0) ||
|
|
800
801
|
Number(metadata.buildId || 0) ||
|
|
@@ -804,7 +805,7 @@ export function assertLocalProjectCanBeSaved(localProject) {
|
|
|
804
805
|
`This is a read-only Lumine reference${sourceBuildId ? ` for Build ${sourceBuildId}` : ""}. Run \`lumine fork${sourceBuildId ? ` ${sourceBuildId}` : ""}\` to create an editable workspace.`,
|
|
805
806
|
);
|
|
806
807
|
}
|
|
807
|
-
if (
|
|
808
|
+
if (readOnlyKind === "server") {
|
|
808
809
|
throw new Error(
|
|
809
810
|
"This Lumine checkout is read-only for the current CLI login. Pull or diff it for review; project-owner branch edits must go through merge or replace-main.",
|
|
810
811
|
);
|
|
@@ -819,6 +820,18 @@ export function isReadOnlyReferenceMetadata(metadata) {
|
|
|
819
820
|
);
|
|
820
821
|
}
|
|
821
822
|
|
|
823
|
+
export function isReadOnlyProjectMetadata(metadata) {
|
|
824
|
+
return readOnlyProjectMetadataKind(metadata) !== null;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
export function readOnlyProjectMetadataKind(metadata) {
|
|
828
|
+
if (metadata?.mainCheckout === true) return "main";
|
|
829
|
+
if (metadata?.versionCheckout === true) return "version";
|
|
830
|
+
if (isReadOnlyReferenceMetadata(metadata)) return "reference";
|
|
831
|
+
if (metadata?.build?.canWrite === false) return "server";
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
|
|
822
835
|
export function resolveLocalProjectFilePath({ rootDir, projectPath }) {
|
|
823
836
|
const relativePath = projectPathToRelativePath(projectPath);
|
|
824
837
|
const root = path.resolve(rootDir);
|