@lunora/cli 1.0.0-alpha.22 → 1.0.0-alpha.24
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/bin.mjs +1 -1
- package/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.mjs +2 -2
- package/dist/packem_chunks/handler.mjs +15 -5
- package/dist/packem_chunks/handler10.mjs +8 -14
- package/dist/packem_chunks/handler11.mjs +19 -189
- package/dist/packem_chunks/handler12.mjs +176 -115
- package/dist/packem_chunks/handler13.mjs +118 -52
- package/dist/packem_chunks/handler14.mjs +50 -43
- package/dist/packem_chunks/handler15.mjs +46 -67
- package/dist/packem_chunks/handler16.mjs +73 -37
- package/dist/packem_chunks/handler17.mjs +38 -100
- package/dist/packem_chunks/handler18.mjs +87 -152
- package/dist/packem_chunks/handler19.mjs +148 -67
- package/dist/packem_chunks/handler2.mjs +1 -1
- package/dist/packem_chunks/handler20.mjs +71 -76
- package/dist/packem_chunks/handler21.mjs +71 -288
- package/dist/packem_chunks/handler3.mjs +1 -1
- package/dist/packem_chunks/handler4.mjs +1 -1
- package/dist/packem_chunks/handler5.mjs +1 -1
- package/dist/packem_chunks/handler6.mjs +1 -1
- package/dist/packem_chunks/handler7.mjs +1 -1
- package/dist/packem_chunks/handler8.mjs +1 -1
- package/dist/packem_chunks/handler9.mjs +311 -12
- package/dist/packem_chunks/planDevCommand.mjs +2 -2
- package/dist/packem_chunks/runCodegenCommand.mjs +1 -1
- package/dist/packem_chunks/runDeployCommand.mjs +103 -13
- package/dist/packem_chunks/runInitCommand.mjs +44 -6
- package/dist/packem_chunks/runMigrateGenerateCommand.mjs +1 -1
- package/dist/packem_chunks/runResetCommand.mjs +2 -2
- package/dist/packem_chunks/runRpcCommand.mjs +1 -1
- package/dist/packem_shared/{COMMANDS-D3h9Iwvl.mjs → COMMANDS-B0ftFD_3.mjs} +20 -16
- package/dist/packem_shared/{command-BC30oSBW.mjs → command-D3lB_4Az.mjs} +5 -0
- package/dist/packem_shared/{commands-hl0mRqqg.mjs → commands-B-gR09Z_.mjs} +1 -1
- package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
- package/dist/packem_shared/{runAddCommand-vJdgiR5t.mjs → runAddCommand-bnY6-HKb.mjs} +1 -1
- package/dist/packem_shared/{storage-B7hHSTZP.mjs → storage-BIsph-Vk.mjs} +1 -1
- package/dist/packem_shared/{tui-prompts-M6OWsuyw.mjs → tui-prompts-BjEN8XgP.mjs} +2 -7
- package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +47 -0
- package/package.json +3 -3
|
@@ -1,79 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
|
|
5
|
-
import { r as runSchemaDriftGate } from '../packem_shared/schema-drift-gate-BtBt0as0.mjs';
|
|
1
|
+
import { readLinkedProject } from '@lunora/config';
|
|
2
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
3
|
+
import { defaultSpawner } from '../packem_shared/createRecordingSpawner-DxI3mebw.mjs';
|
|
6
4
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const inferred = await inferLunoraBindings({ projectRoot: cwd });
|
|
10
|
-
const reconciled = reconcileWranglerBindings(cwd, inferred);
|
|
11
|
-
if (reconciled.changed) {
|
|
12
|
-
logger.success(`provisioned bindings: ${reconciled.added.join(", ")} → ${reconciled.wranglerPath ?? "wrangler.jsonc"}`);
|
|
13
|
-
}
|
|
14
|
-
for (const warning of reconciled.warnings) {
|
|
15
|
-
logger.warn(warning);
|
|
16
|
-
}
|
|
17
|
-
} catch (error) {
|
|
18
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
-
logger.warn(`binding inference skipped: ${message}`);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const runPrepareCommand = async (options) => {
|
|
5
|
+
const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
|
|
6
|
+
const runLogsCommand = async (options) => {
|
|
23
7
|
const cwd = options.cwd ?? process.cwd();
|
|
24
|
-
options.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
8
|
+
if (options.format !== void 0 && !LOG_FORMATS.has(options.format)) {
|
|
9
|
+
options.logger.error(`logs: unknown --format "${options.format}" — expected pretty | json`);
|
|
10
|
+
return { code: 1, descriptor: void 0, error: "invalid format" };
|
|
11
|
+
}
|
|
12
|
+
const args = ["exec", "wrangler", "tail"];
|
|
13
|
+
if (options.worker !== void 0) {
|
|
14
|
+
args.push(options.worker);
|
|
15
|
+
}
|
|
16
|
+
const env = options.env ?? readLinkedProject(cwd)?.env;
|
|
17
|
+
if (env !== void 0) {
|
|
18
|
+
args.push("--env", env);
|
|
19
|
+
}
|
|
20
|
+
if (options.format !== void 0) {
|
|
21
|
+
args.push("--format", options.format);
|
|
22
|
+
}
|
|
23
|
+
if (options.status !== void 0) {
|
|
24
|
+
args.push("--status", options.status);
|
|
37
25
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
codegen,
|
|
41
|
-
logger: options.logger,
|
|
42
|
-
updateBaseline: options.updateSchemaBaseline === true
|
|
43
|
-
});
|
|
44
|
-
if (gate.blocked) {
|
|
45
|
-
return {
|
|
46
|
-
code: 1,
|
|
47
|
-
error: "schema drift gate blocked prepare",
|
|
48
|
-
schemaDrift: { blocked: true, reason: gate.reason },
|
|
49
|
-
validation: { problems: [], wranglerPath: void 0 }
|
|
50
|
-
};
|
|
26
|
+
if (options.search !== void 0) {
|
|
27
|
+
args.push("--search", options.search);
|
|
51
28
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (validation.problems.length > 0) {
|
|
55
|
-
options.logger.error("wrangler.jsonc validation failed:");
|
|
56
|
-
for (const problem of validation.problems) {
|
|
57
|
-
options.logger.error(` - ${problem}`);
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
code: 1,
|
|
61
|
-
error: "wrangler validation failed",
|
|
62
|
-
validation
|
|
63
|
-
};
|
|
29
|
+
if (options.temporary) {
|
|
30
|
+
args.push("--temporary");
|
|
64
31
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
32
|
+
const descriptor = {
|
|
33
|
+
args,
|
|
34
|
+
command: "pnpm",
|
|
35
|
+
cwd
|
|
36
|
+
};
|
|
37
|
+
options.logger.info(`tailing logs via ${descriptor.command} ${descriptor.args.join(" ")}`);
|
|
38
|
+
const spawner = options.spawner ?? defaultSpawner;
|
|
39
|
+
const result = await spawner(descriptor);
|
|
40
|
+
return {
|
|
41
|
+
code: result.code,
|
|
42
|
+
descriptor
|
|
43
|
+
};
|
|
68
44
|
};
|
|
69
45
|
const execute = defineHandler(
|
|
70
|
-
({ cwd, logger, options }) =>
|
|
71
|
-
allowSchemaDrift: options.allowSchemaDrift === true,
|
|
72
|
-
apiSpec: parseApiSpec(options.apiSpec),
|
|
46
|
+
({ argument, cwd, logger, options }) => runLogsCommand({
|
|
73
47
|
cwd,
|
|
48
|
+
env: options.env,
|
|
49
|
+
format: options.format,
|
|
74
50
|
logger,
|
|
75
|
-
|
|
51
|
+
search: options.search,
|
|
52
|
+
status: options.status,
|
|
53
|
+
temporary: options.temporary === true,
|
|
54
|
+
worker: argument[0]
|
|
76
55
|
})
|
|
77
56
|
);
|
|
78
57
|
|
|
79
|
-
export { execute,
|
|
58
|
+
export { execute, runLogsCommand };
|
|
@@ -1,43 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { runCodegen } from '@lunora/codegen';
|
|
2
|
+
import { validateWranglerProject, inferLunoraBindings, reconcileWranglerBindings } from '@lunora/config';
|
|
3
|
+
import { p as parseApiSpec } from '../packem_shared/api-spec-CtA6ilu4.mjs';
|
|
4
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
5
|
+
import { r as runSchemaDriftGate } from '../packem_shared/schema-drift-gate-BtBt0as0.mjs';
|
|
3
6
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
overwrite: options.overwrite === true,
|
|
18
|
-
ref: options.ref,
|
|
19
|
-
source: options.source,
|
|
20
|
-
yes: options.yes === true
|
|
21
|
-
});
|
|
7
|
+
const provisionBindings = async (cwd, logger) => {
|
|
8
|
+
try {
|
|
9
|
+
const inferred = await inferLunoraBindings({ projectRoot: cwd });
|
|
10
|
+
const reconciled = reconcileWranglerBindings(cwd, inferred);
|
|
11
|
+
if (reconciled.changed) {
|
|
12
|
+
logger.success(`provisioned bindings: ${reconciled.added.join(", ")} → ${reconciled.wranglerPath ?? "wrangler.jsonc"}`);
|
|
13
|
+
}
|
|
14
|
+
for (const warning of reconciled.warnings) {
|
|
15
|
+
logger.warn(warning);
|
|
16
|
+
}
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
logger.warn(`binding inference skipped: ${message}`);
|
|
22
20
|
}
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
};
|
|
22
|
+
const runPrepareCommand = async (options) => {
|
|
23
|
+
const cwd = options.cwd ?? process.cwd();
|
|
24
|
+
options.logger.info("running codegen");
|
|
25
|
+
let codegen;
|
|
26
|
+
try {
|
|
27
|
+
codegen = runCodegen({ apiSpec: options.apiSpec, projectRoot: cwd });
|
|
28
|
+
options.logger.success("codegen complete");
|
|
29
|
+
} catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
options.logger.error(`codegen failed: ${message}`);
|
|
32
|
+
return {
|
|
33
|
+
code: 1,
|
|
34
|
+
error: `codegen failed: ${message}`,
|
|
35
|
+
validation: { problems: [], wranglerPath: void 0 }
|
|
36
|
+
};
|
|
25
37
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
const gate = runSchemaDriftGate({
|
|
39
|
+
allowDrift: options.allowSchemaDrift === true,
|
|
40
|
+
codegen,
|
|
41
|
+
logger: options.logger,
|
|
42
|
+
updateBaseline: options.updateSchemaBaseline === true
|
|
43
|
+
});
|
|
44
|
+
if (gate.blocked) {
|
|
45
|
+
return {
|
|
46
|
+
code: 1,
|
|
47
|
+
error: "schema drift gate blocked prepare",
|
|
48
|
+
schemaDrift: { blocked: true, reason: gate.reason },
|
|
49
|
+
validation: { problems: [], wranglerPath: void 0 }
|
|
50
|
+
};
|
|
35
51
|
}
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
await provisionBindings(cwd, options.logger);
|
|
53
|
+
const validation = validateWranglerProject({ projectRoot: cwd });
|
|
54
|
+
if (validation.problems.length > 0) {
|
|
55
|
+
options.logger.error("wrangler.jsonc validation failed:");
|
|
56
|
+
for (const problem of validation.problems) {
|
|
57
|
+
options.logger.error(` - ${problem}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
code: 1,
|
|
61
|
+
error: "wrangler validation failed",
|
|
62
|
+
validation
|
|
63
|
+
};
|
|
38
64
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
65
|
+
gate.rebless?.();
|
|
66
|
+
options.logger.success("project is ready to deploy");
|
|
67
|
+
return { code: 0, validation };
|
|
68
|
+
};
|
|
69
|
+
const execute = defineHandler(
|
|
70
|
+
({ cwd, logger, options }) => runPrepareCommand({
|
|
71
|
+
allowSchemaDrift: options.allowSchemaDrift === true,
|
|
72
|
+
apiSpec: parseApiSpec(options.apiSpec),
|
|
73
|
+
cwd,
|
|
74
|
+
logger,
|
|
75
|
+
updateSchemaBaseline: options.updateSchemaBaseline === true
|
|
76
|
+
})
|
|
77
|
+
);
|
|
42
78
|
|
|
43
|
-
export { execute };
|
|
79
|
+
export { execute, runPrepareCommand };
|
|
@@ -1,105 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { AGENT_RULES_DIR, detectAgentRules, LUNORA_SKILL_NAMES } from '@lunora/config';
|
|
4
|
-
import { join, relative, dirname } from '@visulima/path';
|
|
5
|
-
import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
|
|
1
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
2
|
+
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-B-gR09Z_.mjs';
|
|
6
3
|
|
|
7
|
-
const resolveBundledSkillsDirectory = (startDirectory = dirname(fileURLToPath(import.meta.url))) => {
|
|
8
|
-
let directory = startDirectory;
|
|
9
|
-
for (let index = 0; index < 6; index += 1) {
|
|
10
|
-
const packageJson = join(directory, "package.json");
|
|
11
|
-
if (existsSync(packageJson)) {
|
|
12
|
-
try {
|
|
13
|
-
const parsed = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
14
|
-
if (parsed.name === "@lunora/cli") {
|
|
15
|
-
const skills = join(directory, "skills");
|
|
16
|
-
return existsSync(skills) ? skills : void 0;
|
|
17
|
-
}
|
|
18
|
-
} catch {
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
const parent = dirname(directory);
|
|
22
|
-
if (parent === directory) {
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
directory = parent;
|
|
26
|
-
}
|
|
27
|
-
return void 0;
|
|
28
|
-
};
|
|
29
|
-
const listBundledSkills = (skillsDirectory) => readdirSync(skillsDirectory).filter((name) => {
|
|
30
|
-
const directory = join(skillsDirectory, name);
|
|
31
|
-
return statSync(directory).isDirectory() && existsSync(join(directory, "SKILL.md"));
|
|
32
|
-
});
|
|
33
|
-
const copySkill = (source, destination, overwrite) => {
|
|
34
|
-
mkdirSync(destination, { recursive: true });
|
|
35
|
-
let wrote = false;
|
|
36
|
-
for (const entry of readdirSync(source)) {
|
|
37
|
-
const from = join(source, entry);
|
|
38
|
-
const to = join(destination, entry);
|
|
39
|
-
if (statSync(from).isDirectory()) {
|
|
40
|
-
wrote = copySkill(from, to, overwrite) || wrote;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (existsSync(to) && !overwrite) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
writeFileSync(to, readFileSync(from));
|
|
47
|
-
wrote = true;
|
|
48
|
-
}
|
|
49
|
-
return wrote;
|
|
50
|
-
};
|
|
51
|
-
const runRulesInstall = (options) => {
|
|
52
|
-
const cwd = options.cwd ?? process.cwd();
|
|
53
|
-
const overwrite = options.overwrite === true;
|
|
54
|
-
const skillsDirectory = resolveBundledSkillsDirectory();
|
|
55
|
-
if (skillsDirectory === void 0) {
|
|
56
|
-
options.logger.error("rules: could not locate the bundled skills (is @lunora/cli installed correctly?).");
|
|
57
|
-
return { code: 1, installed: [], skipped: [] };
|
|
58
|
-
}
|
|
59
|
-
const installed = [];
|
|
60
|
-
const skipped = [];
|
|
61
|
-
for (const name of listBundledSkills(skillsDirectory)) {
|
|
62
|
-
const destination = join(cwd, AGENT_RULES_DIR, name);
|
|
63
|
-
const wrote = copySkill(join(skillsDirectory, name), destination, overwrite);
|
|
64
|
-
if (wrote) {
|
|
65
|
-
installed.push(name);
|
|
66
|
-
} else {
|
|
67
|
-
skipped.push(name);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const target = relative(cwd, join(cwd, AGENT_RULES_DIR)) || AGENT_RULES_DIR;
|
|
71
|
-
if (installed.length > 0) {
|
|
72
|
-
options.logger.success(`Installed ${String(installed.length)} Lunora skill(s) into ${target}/: ${installed.join(", ")}.`);
|
|
73
|
-
}
|
|
74
|
-
if (skipped.length > 0) {
|
|
75
|
-
options.logger.info(`Skipped ${String(skipped.length)} existing skill(s) (re-run with --overwrite to replace): ${skipped.join(", ")}.`);
|
|
76
|
-
}
|
|
77
|
-
options.logger.info("Your AI coding agent will pick these up automatically. Start with the `lunora` skill.");
|
|
78
|
-
return { code: 0, installed, skipped };
|
|
79
|
-
};
|
|
80
|
-
const runRulesCheck = (options) => {
|
|
81
|
-
const cwd = options.cwd ?? process.cwd();
|
|
82
|
-
const status = detectAgentRules(cwd);
|
|
83
|
-
if (status.installed) {
|
|
84
|
-
options.logger.success(`Lunora agent rules are installed (${String(status.present.length)}/${String(LUNORA_SKILL_NAMES.length)} skills).`);
|
|
85
|
-
if (status.missing.length > 0) {
|
|
86
|
-
options.logger.info(`Missing: ${status.missing.join(", ")}. Run \`lunora rules install\` to add them.`);
|
|
87
|
-
}
|
|
88
|
-
return { code: 0, installed: status.present, skipped: [] };
|
|
89
|
-
}
|
|
90
|
-
options.logger.warn("Lunora agent rules are not installed. Run `lunora rules install` so your AI agent knows how to use Lunora.");
|
|
91
|
-
return { code: options.strict === true ? 1 : 0, installed: status.present, skipped: [] };
|
|
92
|
-
};
|
|
93
4
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
94
|
-
const subcommand = argument[0]
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
5
|
+
const subcommand = argument[0];
|
|
6
|
+
const names = argument.slice(1);
|
|
7
|
+
if (subcommand === "add") {
|
|
8
|
+
return runAddCommand({
|
|
9
|
+
allowUnsafeSource: options.allowUnsafeSource === true,
|
|
10
|
+
cwd,
|
|
11
|
+
diff: options.diff === true,
|
|
12
|
+
dryRun: options.dryRun === true,
|
|
13
|
+
from: options.from,
|
|
14
|
+
json: options.json === true,
|
|
15
|
+
logger,
|
|
16
|
+
names,
|
|
17
|
+
overwrite: options.overwrite === true,
|
|
18
|
+
ref: options.ref,
|
|
19
|
+
source: options.source,
|
|
20
|
+
yes: options.yes === true
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (subcommand === "list") {
|
|
24
|
+
return runAddCommand({ cwd, from: options.from, json: options.json === true, list: true, logger, names: [], ref: options.ref, source: options.source });
|
|
25
|
+
}
|
|
26
|
+
if (subcommand === "view") {
|
|
27
|
+
return runRegistryViewCommand({
|
|
28
|
+
allowUnsafeSource: options.allowUnsafeSource === true,
|
|
29
|
+
from: options.from,
|
|
30
|
+
logger,
|
|
31
|
+
names,
|
|
32
|
+
ref: options.ref,
|
|
33
|
+
source: options.source
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (subcommand === "build") {
|
|
37
|
+
return runBuildIndexCommand({ check: options.check === true, from: options.from, logger, out: options.out });
|
|
38
|
+
}
|
|
39
|
+
logger.error("registry: unknown subcommand. Usage: lunora registry <add|list|view|build> [names…]");
|
|
102
40
|
return { code: 1 };
|
|
103
41
|
});
|
|
104
42
|
|
|
105
|
-
export { execute
|
|
43
|
+
export { execute };
|
|
@@ -1,170 +1,105 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { join } from '@visulima/path';
|
|
7
|
-
import { Project } from 'ts-morph';
|
|
8
|
-
import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
|
|
9
|
-
import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
10
|
-
import { b as tuiConfirm } from '../packem_shared/tui-prompts-M6OWsuyw.mjs';
|
|
11
|
-
import { runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
|
|
12
|
-
import { runResetCommand } from './runResetCommand.mjs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { AGENT_RULES_DIR, detectAgentRules, LUNORA_SKILL_NAMES } from '@lunora/config';
|
|
4
|
+
import { join, relative, dirname } from '@visulima/path';
|
|
5
|
+
import { d as defineHandler } from '../packem_shared/command-D3lB_4Az.mjs';
|
|
13
6
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
const seedFailure = (code) => {
|
|
35
|
-
return { code, conflicts: 0, generated: 0, inserted: 0, ndjson: "" };
|
|
36
|
-
};
|
|
37
|
-
const guardSeedTargets = (options, schemaPath) => {
|
|
38
|
-
if (!existsSync(schemaPath)) {
|
|
39
|
-
options.logger.error(`schema not found: ${schemaPath} — run \`vis generate lunora-table --name=<name>\` to create one`);
|
|
40
|
-
return seedFailure(1);
|
|
41
|
-
}
|
|
42
|
-
if (options.reset === true && (options.prod === true || !isLocalUrl(options.url))) {
|
|
43
|
-
options.logger.error("--reset only clears local .wrangler/state and cannot be combined with --prod or a remote --url");
|
|
44
|
-
return seedFailure(1);
|
|
7
|
+
const resolveBundledSkillsDirectory = (startDirectory = dirname(fileURLToPath(import.meta.url))) => {
|
|
8
|
+
let directory = startDirectory;
|
|
9
|
+
for (let index = 0; index < 6; index += 1) {
|
|
10
|
+
const packageJson = join(directory, "package.json");
|
|
11
|
+
if (existsSync(packageJson)) {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
14
|
+
if (parsed.name === "@lunora/cli") {
|
|
15
|
+
const skills = join(directory, "skills");
|
|
16
|
+
return existsSync(skills) ? skills : void 0;
|
|
17
|
+
}
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const parent = dirname(directory);
|
|
22
|
+
if (parent === directory) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
directory = parent;
|
|
45
26
|
}
|
|
46
27
|
return void 0;
|
|
47
28
|
};
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
const conflicts = result.body?.conflicts ?? 0;
|
|
63
|
-
if (conflicts > 0) {
|
|
64
|
-
options.logger.warn(
|
|
65
|
-
`${String(conflicts)} row(s) skipped — their _id already exists. Seeding is deterministic; re-run with --reset to wipe local state first, or a different --seed for fresh ids.`
|
|
66
|
-
);
|
|
29
|
+
const listBundledSkills = (skillsDirectory) => readdirSync(skillsDirectory).filter((name) => {
|
|
30
|
+
const directory = join(skillsDirectory, name);
|
|
31
|
+
return statSync(directory).isDirectory() && existsSync(join(directory, "SKILL.md"));
|
|
32
|
+
});
|
|
33
|
+
const copySkill = (source, destination, overwrite) => {
|
|
34
|
+
mkdirSync(destination, { recursive: true });
|
|
35
|
+
let wrote = false;
|
|
36
|
+
for (const entry of readdirSync(source)) {
|
|
37
|
+
const from = join(source, entry);
|
|
38
|
+
const to = join(destination, entry);
|
|
39
|
+
if (statSync(from).isDirectory()) {
|
|
40
|
+
wrote = copySkill(from, to, overwrite) || wrote;
|
|
41
|
+
continue;
|
|
67
42
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
const validateSeedTable = (options, ir) => {
|
|
75
|
-
if (options.table === void 0 || ir.tables.some((table) => table.name === options.table)) {
|
|
76
|
-
return void 0;
|
|
43
|
+
if (existsSync(to) && !overwrite) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
writeFileSync(to, readFileSync(from));
|
|
47
|
+
wrote = true;
|
|
77
48
|
}
|
|
78
|
-
|
|
79
|
-
options.logger.error(`unknown table "${options.table}" — schema defines: ${available || "(no tables)"}`);
|
|
80
|
-
return seedFailure(1);
|
|
49
|
+
return wrote;
|
|
81
50
|
};
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
const runRulesInstall = (options) => {
|
|
52
|
+
const cwd = options.cwd ?? process.cwd();
|
|
53
|
+
const overwrite = options.overwrite === true;
|
|
54
|
+
const skillsDirectory = resolveBundledSkillsDirectory();
|
|
55
|
+
if (skillsDirectory === void 0) {
|
|
56
|
+
options.logger.error("rules: could not locate the bundled skills (is @lunora/cli installed correctly?).");
|
|
57
|
+
return { code: 1, installed: [], skipped: [] };
|
|
58
|
+
}
|
|
59
|
+
const installed = [];
|
|
60
|
+
const skipped = [];
|
|
61
|
+
for (const name of listBundledSkills(skillsDirectory)) {
|
|
62
|
+
const destination = join(cwd, AGENT_RULES_DIR, name);
|
|
63
|
+
const wrote = copySkill(join(skillsDirectory, name), destination, overwrite);
|
|
64
|
+
if (wrote) {
|
|
65
|
+
installed.push(name);
|
|
66
|
+
} else {
|
|
67
|
+
skipped.push(name);
|
|
68
|
+
}
|
|
86
69
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
70
|
+
const target = relative(cwd, join(cwd, AGENT_RULES_DIR)) || AGENT_RULES_DIR;
|
|
71
|
+
if (installed.length > 0) {
|
|
72
|
+
options.logger.success(`Installed ${String(installed.length)} Lunora skill(s) into ${target}/: ${installed.join(", ")}.`);
|
|
90
73
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!confirmed) {
|
|
94
|
-
options.logger.info("seed: aborted");
|
|
95
|
-
return seedFailure(1);
|
|
74
|
+
if (skipped.length > 0) {
|
|
75
|
+
options.logger.info(`Skipped ${String(skipped.length)} existing skill(s) (re-run with --overwrite to replace): ${skipped.join(", ")}.`);
|
|
96
76
|
}
|
|
97
|
-
|
|
77
|
+
options.logger.info("Your AI coding agent will pick these up automatically. Start with the `lunora` skill.");
|
|
78
|
+
return { code: 0, installed, skipped };
|
|
98
79
|
};
|
|
99
|
-
const
|
|
80
|
+
const runRulesCheck = (options) => {
|
|
100
81
|
const cwd = options.cwd ?? process.cwd();
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
107
|
-
const ir = discoverSchema(project, schemaPath);
|
|
108
|
-
const unknownTable = validateSeedTable(options, ir);
|
|
109
|
-
if (unknownTable !== void 0) {
|
|
110
|
-
return unknownTable;
|
|
111
|
-
}
|
|
112
|
-
const schema = schemaFromIr(ir);
|
|
113
|
-
const plan = seedPlan(schema, {
|
|
114
|
-
defaultCount: options.count ?? 10,
|
|
115
|
-
only: options.table === void 0 ? void 0 : [options.table],
|
|
116
|
-
seed: options.seed ?? 0
|
|
117
|
-
});
|
|
118
|
-
const lines = [];
|
|
119
|
-
for (const { rows, table } of plan) {
|
|
120
|
-
for (const row of rows) {
|
|
121
|
-
lines.push(JSON.stringify({ doc: row, table }, ndjsonReplacer));
|
|
82
|
+
const status = detectAgentRules(cwd);
|
|
83
|
+
if (status.installed) {
|
|
84
|
+
options.logger.success(`Lunora agent rules are installed (${String(status.present.length)}/${String(LUNORA_SKILL_NAMES.length)} skills).`);
|
|
85
|
+
if (status.missing.length > 0) {
|
|
86
|
+
options.logger.info(`Missing: ${status.missing.join(", ")}. Run \`lunora rules install\` to add them.`);
|
|
122
87
|
}
|
|
88
|
+
return { code: 0, installed: status.present, skipped: [] };
|
|
123
89
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
options.logger.info(`generated ${String(generated)} row(s) across ${String(plan.length)} table(s) — dry run, nothing inserted`);
|
|
132
|
-
return { code: 0, conflicts: 0, generated, inserted: 0, ndjson };
|
|
133
|
-
}
|
|
134
|
-
if (options.reset === true) {
|
|
135
|
-
const reset = await runResetCommand({ cwd, logger: options.logger, yes: true });
|
|
136
|
-
if (reset.code !== 0) {
|
|
137
|
-
return { code: reset.code, conflicts: 0, generated, inserted: 0, ndjson };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (generated === 0) {
|
|
141
|
-
options.logger.warn("no rows generated — nothing to insert");
|
|
142
|
-
return { code: 0, conflicts: 0, generated: 0, inserted: 0, ndjson };
|
|
90
|
+
options.logger.warn("Lunora agent rules are not installed. Run `lunora rules install` so your AI agent knows how to use Lunora.");
|
|
91
|
+
return { code: options.strict === true ? 1 : 0, installed: status.present, skipped: [] };
|
|
92
|
+
};
|
|
93
|
+
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
94
|
+
const subcommand = argument[0] ?? "check";
|
|
95
|
+
if (subcommand === "install") {
|
|
96
|
+
return runRulesInstall({ cwd, logger, overwrite: options.overwrite === true });
|
|
143
97
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return aborted;
|
|
98
|
+
if (subcommand === "check") {
|
|
99
|
+
return runRulesCheck({ cwd, logger, strict: options.strict === true });
|
|
147
100
|
}
|
|
148
|
-
|
|
149
|
-
};
|
|
150
|
-
const execute = defineHandler(async ({ cwd, logger, options }) => {
|
|
151
|
-
const result = await runSeedCommand({
|
|
152
|
-
batchSize: options.batchSize,
|
|
153
|
-
count: options.count,
|
|
154
|
-
cwd,
|
|
155
|
-
dryRun: options.dryRun === true,
|
|
156
|
-
logger,
|
|
157
|
-
prod: options.prod === true,
|
|
158
|
-
reset: options.reset === true,
|
|
159
|
-
seed: options.seed,
|
|
160
|
-
table: options.table,
|
|
161
|
-
token: options.token,
|
|
162
|
-
// Resolve the link here (only under --prod) so seed's own remote/confirm
|
|
163
|
-
// logic and the downstream import both see the same effective target.
|
|
164
|
-
url: resolveProductionWorkerUrl({ cwd, prod: options.prod === true, url: options.url }),
|
|
165
|
-
yes: options.yes === true
|
|
166
|
-
});
|
|
167
|
-
return { code: result.code };
|
|
101
|
+
logger.error("rules: unknown subcommand. Usage: lunora rules <install|check>");
|
|
102
|
+
return { code: 1 };
|
|
168
103
|
});
|
|
169
104
|
|
|
170
|
-
export { execute,
|
|
105
|
+
export { execute, resolveBundledSkillsDirectory, runRulesCheck, runRulesInstall };
|