@stage5/lumine 0.1.7 → 0.1.8
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 +9 -0
- package/bin/lumine.js +102 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@ Launch Twinkle Lumine builds from any terminal.
|
|
|
5
5
|
```bash
|
|
6
6
|
npx @stage5/lumine@latest
|
|
7
7
|
npx @stage5/lumine@latest login
|
|
8
|
+
npx @stage5/lumine@latest new "Daily Reflection App"
|
|
9
|
+
npx @stage5/lumine@latest new --title "Daily Reflection App" --description "Private journal with streaks"
|
|
8
10
|
npx @stage5/lumine@latest projects
|
|
9
11
|
npx @stage5/lumine@latest explore --sort forks
|
|
10
12
|
npx @stage5/lumine@latest reference https://www.twin-kle.com/app/123
|
|
@@ -19,6 +21,13 @@ Run `lumine` with no subcommand for the easiest flow: sign in when needed,
|
|
|
19
21
|
choose one of your owned or team projects, and pull the saved project files into
|
|
20
22
|
a local folder.
|
|
21
23
|
|
|
24
|
+
Use `lumine new` to create a new Twinkle Build with the same website create
|
|
25
|
+
route used by Build Studio. The CLI asks for a title when one is not passed and
|
|
26
|
+
asks for an optional description. It creates the Build and pulls an editable
|
|
27
|
+
local workspace, but it does not auto-start a Lumine greeting or prompt run, so
|
|
28
|
+
creating a project from the CLI does not spend AI battery. Add project files
|
|
29
|
+
locally, including `/index.html`, then run `lumine save`.
|
|
30
|
+
|
|
22
31
|
For team projects, Lumine mirrors the website workspace flow: choosing or
|
|
23
32
|
pulling the owner's main project creates or reuses your contribution branch and
|
|
24
33
|
checks out that branch locally. Saves go to your branch, so the project owner
|
package/bin/lumine.js
CHANGED
|
@@ -119,6 +119,7 @@ const COMMANDS = new Set([
|
|
|
119
119
|
"login",
|
|
120
120
|
"logout",
|
|
121
121
|
"whoami",
|
|
122
|
+
"new",
|
|
122
123
|
"projects",
|
|
123
124
|
"explore",
|
|
124
125
|
"select",
|
|
@@ -167,6 +168,10 @@ async function main() {
|
|
|
167
168
|
await whoami(options);
|
|
168
169
|
return;
|
|
169
170
|
}
|
|
171
|
+
if (options.command === "new") {
|
|
172
|
+
await newBuild(options);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
170
175
|
if (options.command === "projects") {
|
|
171
176
|
await projects(options);
|
|
172
177
|
return;
|
|
@@ -314,6 +319,26 @@ async function whoami(options) {
|
|
|
314
319
|
);
|
|
315
320
|
}
|
|
316
321
|
|
|
322
|
+
async function newBuild(options) {
|
|
323
|
+
const title = await resolveNewBuildTitle(options);
|
|
324
|
+
const description = await resolveNewBuildDescription(options);
|
|
325
|
+
const auth = await ensureAuth(options);
|
|
326
|
+
await assertAuthScope({ options, auth, scope: "build:write" });
|
|
327
|
+
const createResult = await createBuild({
|
|
328
|
+
options,
|
|
329
|
+
auth,
|
|
330
|
+
title,
|
|
331
|
+
description,
|
|
332
|
+
});
|
|
333
|
+
const buildId = Number(createResult.build?.id || 0);
|
|
334
|
+
if (!buildId) {
|
|
335
|
+
throw new Error("Twinkle did not return a created Build.");
|
|
336
|
+
}
|
|
337
|
+
const result = await pullBuildFiles({ options, auth, buildId });
|
|
338
|
+
await saveSelectedBuild({ options, auth, build: result.build });
|
|
339
|
+
printNewBuildResult({ createResult, pullResult: result });
|
|
340
|
+
}
|
|
341
|
+
|
|
317
342
|
async function workspace(options) {
|
|
318
343
|
const auth = await ensureAuth(options);
|
|
319
344
|
const selectedBuild = options.target
|
|
@@ -774,6 +799,19 @@ async function forkBuild({ options, auth, buildId }) {
|
|
|
774
799
|
});
|
|
775
800
|
}
|
|
776
801
|
|
|
802
|
+
async function createBuild({ options, auth, title, description }) {
|
|
803
|
+
return await requestJson({
|
|
804
|
+
method: "POST",
|
|
805
|
+
url: `${options.apiUrl}/build/create`,
|
|
806
|
+
authToken: auth.token,
|
|
807
|
+
body: {
|
|
808
|
+
title,
|
|
809
|
+
...(description ? { description } : {}),
|
|
810
|
+
},
|
|
811
|
+
timeoutMs: options.timeoutMs,
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
777
815
|
async function saveProjectFiles({ options, auth, buildId, files, summary }) {
|
|
778
816
|
return await requestJson({
|
|
779
817
|
method: "PUT",
|
|
@@ -1517,6 +1555,9 @@ function printPullResult(result) {
|
|
|
1517
1555
|
}
|
|
1518
1556
|
return;
|
|
1519
1557
|
}
|
|
1558
|
+
if (result.fileCount === 0) {
|
|
1559
|
+
console.log("No project files yet. Create /index.html before your first save.");
|
|
1560
|
+
}
|
|
1520
1561
|
console.log('Codex: codex "Read AGENTS.md, then make the requested change."');
|
|
1521
1562
|
console.log(
|
|
1522
1563
|
'Claude Code: claude "Read CLAUDE.md, then make the requested change."',
|
|
@@ -1529,6 +1570,12 @@ function printPullResult(result) {
|
|
|
1529
1570
|
}
|
|
1530
1571
|
}
|
|
1531
1572
|
|
|
1573
|
+
function printNewBuildResult({ createResult, pullResult }) {
|
|
1574
|
+
const build = pullResult.build || createResult.build || {};
|
|
1575
|
+
console.log(`Created ${formatBuildTitle(build)}.`);
|
|
1576
|
+
printPullResult(pullResult);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1532
1579
|
function printReferenceResult(result) {
|
|
1533
1580
|
const build = result.build || {};
|
|
1534
1581
|
const sourceBuildId =
|
|
@@ -1868,6 +1915,7 @@ function parseArgs(args) {
|
|
|
1868
1915
|
const booleanFlags = new Set([
|
|
1869
1916
|
"noOpen",
|
|
1870
1917
|
"open",
|
|
1918
|
+
"noDescription",
|
|
1871
1919
|
"publish",
|
|
1872
1920
|
"save",
|
|
1873
1921
|
"noUpdateCheck",
|
|
@@ -1898,6 +1946,20 @@ function parseArgs(args) {
|
|
|
1898
1946
|
return {
|
|
1899
1947
|
command,
|
|
1900
1948
|
target: raw.url || raw.target || positional[0] || "",
|
|
1949
|
+
title:
|
|
1950
|
+
String(
|
|
1951
|
+
raw.title ||
|
|
1952
|
+
(command === "new" ? positional.join(" ") : ""),
|
|
1953
|
+
).trim() || "",
|
|
1954
|
+
description:
|
|
1955
|
+
Object.prototype.hasOwnProperty.call(raw, "description")
|
|
1956
|
+
? String(raw.description || "").trim()
|
|
1957
|
+
: null,
|
|
1958
|
+
descriptionProvided: Object.prototype.hasOwnProperty.call(
|
|
1959
|
+
raw,
|
|
1960
|
+
"description",
|
|
1961
|
+
),
|
|
1962
|
+
noDescription: parseBoolean(raw.noDescription, false),
|
|
1901
1963
|
searchQuery:
|
|
1902
1964
|
String(
|
|
1903
1965
|
raw.search ||
|
|
@@ -1990,6 +2052,40 @@ async function resolveRequiredBuildIdOrSelected(
|
|
|
1990
2052
|
);
|
|
1991
2053
|
}
|
|
1992
2054
|
|
|
2055
|
+
async function resolveNewBuildTitle(options) {
|
|
2056
|
+
const title = String(options.title || "").trim();
|
|
2057
|
+
if (title) return title;
|
|
2058
|
+
if (!input.isTTY || !output.isTTY) {
|
|
2059
|
+
throw new Error('Pass a title: `lumine new "My Build"` or `lumine new --title "My Build"`.');
|
|
2060
|
+
}
|
|
2061
|
+
const rl = readline.createInterface({ input, output });
|
|
2062
|
+
try {
|
|
2063
|
+
const answer = await rl.question("Build title: ");
|
|
2064
|
+
const normalized = String(answer || "").trim();
|
|
2065
|
+
if (!normalized) {
|
|
2066
|
+
throw new Error("Title is required.");
|
|
2067
|
+
}
|
|
2068
|
+
return normalized;
|
|
2069
|
+
} finally {
|
|
2070
|
+
rl.close();
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
async function resolveNewBuildDescription(options) {
|
|
2075
|
+
if (options.noDescription) return null;
|
|
2076
|
+
if (options.descriptionProvided) {
|
|
2077
|
+
return String(options.description || "").trim() || null;
|
|
2078
|
+
}
|
|
2079
|
+
if (!input.isTTY || !output.isTTY) return null;
|
|
2080
|
+
const rl = readline.createInterface({ input, output });
|
|
2081
|
+
try {
|
|
2082
|
+
const answer = await rl.question("Description (optional): ");
|
|
2083
|
+
return String(answer || "").trim() || null;
|
|
2084
|
+
} finally {
|
|
2085
|
+
rl.close();
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
1993
2089
|
function resolveRequiredBuildId(value) {
|
|
1994
2090
|
const buildId = resolveBuildReference(value).buildId;
|
|
1995
2091
|
if (buildId > 0) return buildId;
|
|
@@ -2170,6 +2266,7 @@ function printHelp() {
|
|
|
2170
2266
|
lumine login
|
|
2171
2267
|
lumine whoami
|
|
2172
2268
|
lumine logout
|
|
2269
|
+
lumine new [title]
|
|
2173
2270
|
lumine projects
|
|
2174
2271
|
lumine explore [search terms]
|
|
2175
2272
|
lumine select [twinkle-build-url]
|
|
@@ -2186,6 +2283,8 @@ function printHelp() {
|
|
|
2186
2283
|
Examples:
|
|
2187
2284
|
npx @stage5/lumine@latest
|
|
2188
2285
|
npx @stage5/lumine@latest login
|
|
2286
|
+
npx @stage5/lumine@latest new "Daily Reflection App"
|
|
2287
|
+
npx @stage5/lumine@latest new --title "Daily Reflection App" --description "Private journal with streaks"
|
|
2189
2288
|
npx @stage5/lumine@latest explore --sort forks
|
|
2190
2289
|
npx @stage5/lumine@latest reference https://www.twin-kle.com/app/123
|
|
2191
2290
|
npx @stage5/lumine@latest fork https://www.twin-kle.com/app/123
|
|
@@ -2203,6 +2302,9 @@ Options:
|
|
|
2203
2302
|
--auth-file <path> Saved login path
|
|
2204
2303
|
--auth-token <token> Override saved login
|
|
2205
2304
|
--dir <path> Directory for pulled project files
|
|
2305
|
+
--title <text> New Build title
|
|
2306
|
+
--description <text> Optional New Build description
|
|
2307
|
+
--no-description Skip the New Build description prompt
|
|
2206
2308
|
--summary <text> Save summary
|
|
2207
2309
|
--search <text> Search public open-source Builds
|
|
2208
2310
|
--sort <sort> Sort open-source Builds: forks, popular, recent
|