@malloy-publisher/create-malloy-package 0.0.27 → 0.0.29
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/index.js +85 -187
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import * as crypto from "node:crypto";
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
5
|
+
import * as fs4 from "node:fs";
|
|
6
|
+
import * as path3 from "node:path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
9
|
|
|
@@ -406,8 +406,8 @@ function staleScaffolderWarning(running, latest) {
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
// src/scaffold.ts
|
|
409
|
-
import * as
|
|
410
|
-
import * as
|
|
409
|
+
import * as fs3 from "node:fs";
|
|
410
|
+
import * as path2 from "node:path";
|
|
411
411
|
|
|
412
412
|
// src/config.ts
|
|
413
413
|
import * as fs from "node:fs";
|
|
@@ -528,126 +528,24 @@ function addPackage(config, envName, pkg) {
|
|
|
528
528
|
return { added: true, envName: env.name };
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
+
// src/scaffold.ts
|
|
532
|
+
import { installSkills, isWithinDirectory } from "@malloy-publisher/skills";
|
|
533
|
+
|
|
531
534
|
// src/skills.ts
|
|
532
|
-
import
|
|
533
|
-
import * as path from "node:path";
|
|
534
|
-
import { skillsDir } from "@malloy-publisher/skills";
|
|
535
|
+
import { countSkills, skillsDir } from "@malloy-publisher/skills";
|
|
535
536
|
function assertSkillsAvailable() {
|
|
536
537
|
if (countSkills(skillsDir) === 0) {
|
|
537
538
|
throw new ScaffoldError(`No Malloy skills found in ${skillsDir}. The @malloy-publisher/skills ` + `install looks incomplete; reinstall create-malloy-package and run again.`);
|
|
538
539
|
}
|
|
539
540
|
}
|
|
540
|
-
function installSkills(targetDir, workspaceRoot) {
|
|
541
|
-
const empty = { installed: 0, skipped: [], refreshed: [], removed: [] };
|
|
542
|
-
if (isSymlink(targetDir)) {
|
|
543
|
-
return {
|
|
544
|
-
...empty,
|
|
545
|
-
refused: "it is a symlink, so writing into it would edit whatever it points at"
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
if (!isWithinDirectory(targetDir, workspaceRoot)) {
|
|
549
|
-
return {
|
|
550
|
-
...empty,
|
|
551
|
-
refused: "it resolves outside the workspace, so writing into it would put " + "files somewhere else on this machine"
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
fs2.mkdirSync(targetDir, { recursive: true });
|
|
555
|
-
const skipped = [];
|
|
556
|
-
const refreshed = [];
|
|
557
|
-
const removed = [];
|
|
558
|
-
let installed = 0;
|
|
559
|
-
for (const entry of fs2.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
560
|
-
if (!entry.isDirectory()) {
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
const source = path.join(skillsDir, entry.name);
|
|
564
|
-
if (!fs2.existsSync(path.join(source, "SKILL.md"))) {
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
const target = path.join(targetDir, entry.name);
|
|
568
|
-
if (isSymlink(target)) {
|
|
569
|
-
skipped.push(entry.name);
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
if (fs2.existsSync(target)) {
|
|
573
|
-
refreshed.push(entry.name);
|
|
574
|
-
for (const relative of pathsNotIn(target, source)) {
|
|
575
|
-
removed.push(path.join(entry.name, relative));
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
fs2.rmSync(target, { recursive: true, force: true });
|
|
579
|
-
fs2.cpSync(source, target, { recursive: true });
|
|
580
|
-
installed += 1;
|
|
581
|
-
}
|
|
582
|
-
return { installed, skipped, refreshed, removed };
|
|
583
|
-
}
|
|
584
|
-
function pathsNotIn(target, source, prefix = "") {
|
|
585
|
-
let entries;
|
|
586
|
-
try {
|
|
587
|
-
entries = fs2.readdirSync(path.join(target, prefix), {
|
|
588
|
-
withFileTypes: true
|
|
589
|
-
});
|
|
590
|
-
} catch {
|
|
591
|
-
return [];
|
|
592
|
-
}
|
|
593
|
-
const missing = [];
|
|
594
|
-
for (const entry of entries) {
|
|
595
|
-
const relative = path.join(prefix, entry.name);
|
|
596
|
-
if (!fs2.existsSync(path.join(source, relative))) {
|
|
597
|
-
missing.push(entry.isDirectory() ? relative + path.sep : relative);
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
if (entry.isDirectory()) {
|
|
601
|
-
missing.push(...pathsNotIn(target, source, relative));
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return missing;
|
|
605
|
-
}
|
|
606
|
-
function countSkills(dir) {
|
|
607
|
-
if (!fs2.existsSync(dir)) {
|
|
608
|
-
return 0;
|
|
609
|
-
}
|
|
610
|
-
return fs2.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs2.existsSync(path.join(dir, entry.name, "SKILL.md"))).length;
|
|
611
|
-
}
|
|
612
|
-
function isWithinDirectory(target, root) {
|
|
613
|
-
const realRoot = realPathOf(root);
|
|
614
|
-
const realTarget = realPathOf(target);
|
|
615
|
-
return realTarget === realRoot || realTarget.startsWith(realRoot + path.sep);
|
|
616
|
-
}
|
|
617
|
-
function realPathOf(target, hops = 0) {
|
|
618
|
-
const resolved = path.resolve(target);
|
|
619
|
-
const parent = path.dirname(resolved);
|
|
620
|
-
if (parent === resolved) {
|
|
621
|
-
return resolved;
|
|
622
|
-
}
|
|
623
|
-
const here = path.join(realPathOf(parent, hops), path.basename(resolved));
|
|
624
|
-
if (hops >= 40) {
|
|
625
|
-
return here;
|
|
626
|
-
}
|
|
627
|
-
try {
|
|
628
|
-
if (!fs2.lstatSync(here).isSymbolicLink()) {
|
|
629
|
-
return here;
|
|
630
|
-
}
|
|
631
|
-
return realPathOf(path.resolve(path.dirname(here), fs2.readlinkSync(here)), hops + 1);
|
|
632
|
-
} catch {
|
|
633
|
-
return here;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
function isSymlink(target) {
|
|
637
|
-
try {
|
|
638
|
-
return fs2.lstatSync(target).isSymbolicLink();
|
|
639
|
-
} catch {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
541
|
|
|
644
542
|
// src/templates.ts
|
|
645
|
-
import * as
|
|
646
|
-
import * as
|
|
543
|
+
import * as fs2 from "node:fs";
|
|
544
|
+
import * as path from "node:path";
|
|
647
545
|
import { fileURLToPath } from "node:url";
|
|
648
|
-
var templatesDir =
|
|
546
|
+
var templatesDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "templates");
|
|
649
547
|
function renderTemplate(name, vars) {
|
|
650
|
-
const raw =
|
|
548
|
+
const raw = fs2.readFileSync(path.join(templatesDir, name), "utf8");
|
|
651
549
|
const referenced = new Set;
|
|
652
550
|
const rendered = raw.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
653
551
|
if (!(key in vars)) {
|
|
@@ -675,7 +573,7 @@ var MCP_PORT = 4040;
|
|
|
675
573
|
var ALT_PUBLISHER_PORT = PUBLISHER_PORT + 100;
|
|
676
574
|
var ALT_MCP_PORT = MCP_PORT + 100;
|
|
677
575
|
var BIND_HOST = "127.0.0.1";
|
|
678
|
-
var SERVER_VERSION = "0.9.
|
|
576
|
+
var SERVER_VERSION = "0.9.3";
|
|
679
577
|
function startCommandFor(envName) {
|
|
680
578
|
return `npx -y @malloy-publisher/server@${SERVER_VERSION} --server_root . ` + `--config ./publisher.config.json --host ${BIND_HOST} ` + `--watch-env ${envName}`;
|
|
681
579
|
}
|
|
@@ -746,8 +644,8 @@ function scaffold(options) {
|
|
|
746
644
|
mcpEntryReplaced: false,
|
|
747
645
|
skillsInstalled: 0
|
|
748
646
|
};
|
|
749
|
-
const configPath =
|
|
750
|
-
const configExists =
|
|
647
|
+
const configPath = path2.join(options.cwd, "publisher.config.json");
|
|
648
|
+
const configExists = fs3.existsSync(configPath);
|
|
751
649
|
const config = configExists ? readConfig(configPath) : defaultConfig(ENV_NAME);
|
|
752
650
|
const targetEnv = targetEnvironment(config, ENV_NAME);
|
|
753
651
|
if (targetEnv !== undefined) {
|
|
@@ -774,9 +672,9 @@ function createPackage(options, result) {
|
|
|
774
672
|
const sourceName = toMalloyIdentifier(name);
|
|
775
673
|
const modelIsIndex = sourceName.toLowerCase() === "index";
|
|
776
674
|
const modelFile = modelIsIndex ? INDEX_MODEL_NAME : `${sourceName}.malloy`;
|
|
777
|
-
const packageDir =
|
|
778
|
-
const packageDirExists =
|
|
779
|
-
if (packageDirExists && !
|
|
675
|
+
const packageDir = path2.join(options.cwd, name);
|
|
676
|
+
const packageDirExists = fs3.existsSync(packageDir);
|
|
677
|
+
if (packageDirExists && !fs3.lstatSync(packageDir).isDirectory()) {
|
|
780
678
|
throw new ScaffoldError(`"${name}" already exists here and is not a directory, so the package ` + `cannot be created there. --force does not help: it overwrites files ` + `inside a package directory, it does not replace something that is ` + `in the way.
|
|
781
679
|
|
|
782
680
|
` + `Choose another name, or move or remove "${name}" first.`);
|
|
@@ -789,25 +687,25 @@ function createPackage(options, result) {
|
|
|
789
687
|
` + `If you passed --force to \`npm create\`, npm read it as one of its ` + `own settings and it never reached this tool. Options need a \`--\` ` + `in front of them there:
|
|
790
688
|
` + ` npm create @malloy-publisher/malloy-package@latest ${name} -- --force`);
|
|
791
689
|
}
|
|
792
|
-
const dataDir =
|
|
690
|
+
const dataDir = path2.join(packageDir, "data");
|
|
793
691
|
assertWithinWorkspace(dataDir, options.cwd, `${name}/data`);
|
|
794
|
-
|
|
795
|
-
writeFile(
|
|
692
|
+
fs3.mkdirSync(dataDir, { recursive: true });
|
|
693
|
+
writeFile(path2.join(packageDir, "publisher.json"), JSON.stringify({ name }, null, 2) + `
|
|
796
694
|
`, options.cwd);
|
|
797
|
-
writeFile(
|
|
695
|
+
writeFile(path2.join(packageDir, "malloy-config.json"), JSON.stringify({
|
|
798
696
|
connections: {
|
|
799
697
|
duckdb: {
|
|
800
698
|
is: "duckdb",
|
|
801
|
-
workingDirectory:
|
|
699
|
+
workingDirectory: path2.resolve(packageDir)
|
|
802
700
|
}
|
|
803
701
|
}
|
|
804
702
|
}, null, 2) + `
|
|
805
703
|
`, options.cwd);
|
|
806
704
|
let dataPath;
|
|
807
705
|
if (options.dataFile !== undefined) {
|
|
808
|
-
const sourceBase =
|
|
706
|
+
const sourceBase = path2.basename(options.dataFile);
|
|
809
707
|
const destBase = sanitizeFileName(sourceBase);
|
|
810
|
-
copyFile(options.dataFile,
|
|
708
|
+
copyFile(options.dataFile, path2.join(dataDir, destBase), options.cwd, `${name}/data/${destBase}`);
|
|
811
709
|
dataPath = `data/${destBase}`;
|
|
812
710
|
if (destBase !== sourceBase) {
|
|
813
711
|
result.dataFileRenamedFrom = sourceBase;
|
|
@@ -816,14 +714,14 @@ function createPackage(options, result) {
|
|
|
816
714
|
if (siblings.length > 0) {
|
|
817
715
|
result.siblingDataFiles = siblings;
|
|
818
716
|
}
|
|
819
|
-
writeFile(
|
|
717
|
+
writeFile(path2.join(packageDir, modelFile), renderTemplate(isSpreadsheet(dataPath) ? "model.custom.xlsx.malloy" : "model.custom.malloy", { sourceName, dataPath }), options.cwd);
|
|
820
718
|
} else {
|
|
821
|
-
copyFile(
|
|
719
|
+
copyFile(path2.join(templatesDir, "sales.csv"), path2.join(dataDir, "sales.csv"), options.cwd, `${name}/data/sales.csv`);
|
|
822
720
|
dataPath = "data/sales.csv";
|
|
823
|
-
writeFile(
|
|
721
|
+
writeFile(path2.join(packageDir, modelFile), renderTemplate("model.default.malloy", { sourceName }), options.cwd);
|
|
824
722
|
}
|
|
825
723
|
if (!modelIsIndex) {
|
|
826
|
-
writeFile(
|
|
724
|
+
writeFile(path2.join(packageDir, INDEX_MODEL_NAME), renderTemplate(INDEX_MODEL_NAME, { sourceName, modelFile }), options.cwd);
|
|
827
725
|
}
|
|
828
726
|
result.packageCreated = true;
|
|
829
727
|
result.packageName = name;
|
|
@@ -862,7 +760,7 @@ function wireWorkspace(options, config, configExists, configPath, packageEntry,
|
|
|
862
760
|
} else {
|
|
863
761
|
result.skipped.push("publisher.config.json");
|
|
864
762
|
}
|
|
865
|
-
result.needsReset = result.packageCreated && (
|
|
763
|
+
result.needsReset = result.packageCreated && (fs3.existsSync(path2.join(cwd, "publisher_data")) || fs3.existsSync(path2.join(cwd, "publisher.db")));
|
|
866
764
|
result.startCommand = startCommandFor(result.envName);
|
|
867
765
|
result.resetCommand = resetCommandFor(result.envName);
|
|
868
766
|
writeWorkspacePackageJson(cwd, options, result);
|
|
@@ -884,8 +782,8 @@ function wireWorkspace(options, config, configExists, configPath, packageEntry,
|
|
|
884
782
|
function writeAgentsFile(cwd, content, options, result) {
|
|
885
783
|
assertBriefingIsRecognisable(content);
|
|
886
784
|
const relPath = "AGENTS.md";
|
|
887
|
-
const target =
|
|
888
|
-
const exists =
|
|
785
|
+
const target = path2.join(cwd, relPath);
|
|
786
|
+
const exists = fs3.existsSync(target);
|
|
889
787
|
const ours = exists && isGeneratedBriefing(target);
|
|
890
788
|
if (!exists || ours || options.force) {
|
|
891
789
|
writeFile(target, content, cwd);
|
|
@@ -895,14 +793,14 @@ function writeAgentsFile(cwd, content, options, result) {
|
|
|
895
793
|
} else if (exists) {
|
|
896
794
|
result.replaced.push(relPath);
|
|
897
795
|
}
|
|
898
|
-
if (
|
|
796
|
+
if (fs3.existsSync(path2.join(cwd, MALLOY_AGENTS_FILE))) {
|
|
899
797
|
result.skipped.push(`${MALLOY_AGENTS_FILE} (left from an earlier run, when AGENTS.md ` + `was not ours to write; AGENTS.md now holds the briefing, so ` + `this copy is stale and can be deleted)`);
|
|
900
798
|
}
|
|
901
799
|
return;
|
|
902
800
|
}
|
|
903
801
|
result.skipped.push(relPath);
|
|
904
|
-
const existed =
|
|
905
|
-
writeFile(
|
|
802
|
+
const existed = fs3.existsSync(path2.join(cwd, MALLOY_AGENTS_FILE));
|
|
803
|
+
writeFile(path2.join(cwd, MALLOY_AGENTS_FILE), content, cwd);
|
|
906
804
|
result.agentsFile = MALLOY_AGENTS_FILE;
|
|
907
805
|
result.written.push(`${MALLOY_AGENTS_FILE} (the Publisher briefing, because this ` + `directory's own AGENTS.md was left alone)`);
|
|
908
806
|
if (existed) {
|
|
@@ -911,7 +809,7 @@ function writeAgentsFile(cwd, content, options, result) {
|
|
|
911
809
|
}
|
|
912
810
|
function isGeneratedBriefing(target) {
|
|
913
811
|
try {
|
|
914
|
-
return
|
|
812
|
+
return fs3.readFileSync(target, "utf8").includes(BRIEFING_MARKER);
|
|
915
813
|
} catch {
|
|
916
814
|
return false;
|
|
917
815
|
}
|
|
@@ -922,27 +820,27 @@ function assertBriefingIsRecognisable(content) {
|
|
|
922
820
|
}
|
|
923
821
|
}
|
|
924
822
|
function installWorkspaceSkills(cwd, result) {
|
|
925
|
-
const relDir =
|
|
926
|
-
const skills = installSkills(
|
|
823
|
+
const relDir = path2.join(".claude", "skills");
|
|
824
|
+
const skills = installSkills(path2.join(cwd, relDir), cwd);
|
|
927
825
|
result.skillsInstalled = skills.installed;
|
|
928
826
|
if (skills.refused) {
|
|
929
|
-
result.skipped.push(`${relDir}${
|
|
827
|
+
result.skipped.push(`${relDir}${path2.sep} (${skills.refused}; no skills were installed)`);
|
|
930
828
|
}
|
|
931
829
|
for (const name of skills.skipped) {
|
|
932
|
-
result.skipped.push(`${
|
|
830
|
+
result.skipped.push(`${path2.join(relDir, name)} (symlink, not written through)`);
|
|
933
831
|
}
|
|
934
832
|
if (skills.refreshed.length > 0) {
|
|
935
833
|
const count = skills.refreshed.length;
|
|
936
|
-
result.replaced.push(`${relDir}${
|
|
834
|
+
result.replaced.push(`${relDir}${path2.sep} (${count} skill${count === 1 ? "" : "s"} ` + `refreshed: every run deletes each skill directory and writes the ` + `bundled copy back, so edits to a skill are reverted and anything ` + `else you put inside one is removed, not just changed)`);
|
|
937
835
|
}
|
|
938
836
|
if (skills.removed.length > 0) {
|
|
939
|
-
const shown2 = skills.removed.slice(0, 5).map((entry) => printable(
|
|
837
|
+
const shown2 = skills.removed.slice(0, 5).map((entry) => printable(path2.join(relDir, entry)));
|
|
940
838
|
const rest = skills.removed.length - shown2.length;
|
|
941
839
|
result.replaced.push(`${shown2.join(", ")}${rest > 0 ? `, and ${rest} more` : ""} (not ` + `part of any bundled skill, so nothing wrote ` + `${shown2.length === 1 && rest === 0 ? "it" : "them"} back)`);
|
|
942
840
|
}
|
|
943
841
|
}
|
|
944
842
|
function assertServablePath(cwd) {
|
|
945
|
-
const absolute =
|
|
843
|
+
const absolute = path2.resolve(cwd);
|
|
946
844
|
for (const character of absolute) {
|
|
947
845
|
if (!isServablePathCharacter(character)) {
|
|
948
846
|
const codePoint = character.codePointAt(0);
|
|
@@ -977,7 +875,7 @@ function assertWorkspacePathsContained(options, mcpConfigPath) {
|
|
|
977
875
|
relatives.push(options.name);
|
|
978
876
|
}
|
|
979
877
|
for (const relative2 of relatives) {
|
|
980
|
-
assertWithinWorkspace(
|
|
878
|
+
assertWithinWorkspace(path2.join(options.cwd, relative2), options.cwd, relative2);
|
|
981
879
|
}
|
|
982
880
|
}
|
|
983
881
|
function assertWithinWorkspace(target, root, label) {
|
|
@@ -991,13 +889,13 @@ function assertNotReservedName(name) {
|
|
|
991
889
|
}
|
|
992
890
|
}
|
|
993
891
|
function readWorkspacePackageJson(cwd) {
|
|
994
|
-
const target =
|
|
995
|
-
if (!
|
|
892
|
+
const target = path2.join(cwd, "package.json");
|
|
893
|
+
if (!fs3.existsSync(target)) {
|
|
996
894
|
return { kind: "absent" };
|
|
997
895
|
}
|
|
998
896
|
let text;
|
|
999
897
|
try {
|
|
1000
|
-
text =
|
|
898
|
+
text = fs3.readFileSync(target, "utf8");
|
|
1001
899
|
} catch (err) {
|
|
1002
900
|
return {
|
|
1003
901
|
kind: "unusable",
|
|
@@ -1262,7 +1160,7 @@ function readMergeablePackageJson(cwd) {
|
|
|
1262
1160
|
}
|
|
1263
1161
|
function writeWorkspacePackageJson(cwd, options, result) {
|
|
1264
1162
|
const relPath = "package.json";
|
|
1265
|
-
const target =
|
|
1163
|
+
const target = path2.join(cwd, relPath);
|
|
1266
1164
|
const manifest = readWorkspacePackageJson(cwd);
|
|
1267
1165
|
if (manifest.kind === "absent") {
|
|
1268
1166
|
writeFile(target, workspacePackageJson(cwd, result), cwd);
|
|
@@ -1296,8 +1194,8 @@ function writeWorkspacePackageJson(cwd, options, result) {
|
|
|
1296
1194
|
}
|
|
1297
1195
|
}
|
|
1298
1196
|
function writeMcpConfig(cwd, relPath, result) {
|
|
1299
|
-
const target =
|
|
1300
|
-
if (!
|
|
1197
|
+
const target = path2.join(cwd, relPath);
|
|
1198
|
+
if (!fs3.existsSync(target)) {
|
|
1301
1199
|
writeFile(target, mcpConfig(), cwd);
|
|
1302
1200
|
result.written.push(relPath);
|
|
1303
1201
|
result.mcpWired = true;
|
|
@@ -1306,7 +1204,7 @@ function writeMcpConfig(cwd, relPath, result) {
|
|
|
1306
1204
|
let parsed;
|
|
1307
1205
|
let parses = true;
|
|
1308
1206
|
try {
|
|
1309
|
-
parsed = parseJson(
|
|
1207
|
+
parsed = parseJson(fs3.readFileSync(target, "utf8"));
|
|
1310
1208
|
} catch {
|
|
1311
1209
|
parses = false;
|
|
1312
1210
|
}
|
|
@@ -1439,7 +1337,7 @@ function isNameableHere(name) {
|
|
|
1439
1337
|
}
|
|
1440
1338
|
function workspacePackageJson(cwd, result) {
|
|
1441
1339
|
return JSON.stringify({
|
|
1442
|
-
name: toNpmName(
|
|
1340
|
+
name: toNpmName(path2.basename(path2.resolve(cwd))),
|
|
1443
1341
|
version: "0.1.0",
|
|
1444
1342
|
private: true,
|
|
1445
1343
|
engines: {
|
|
@@ -1469,8 +1367,8 @@ function workspaceGitignore() {
|
|
|
1469
1367
|
}
|
|
1470
1368
|
function mergeGitignore(cwd, result) {
|
|
1471
1369
|
const relPath = ".gitignore";
|
|
1472
|
-
const target =
|
|
1473
|
-
if (!
|
|
1370
|
+
const target = path2.join(cwd, relPath);
|
|
1371
|
+
if (!fs3.existsSync(target)) {
|
|
1474
1372
|
writeFile(target, workspaceGitignore(), cwd);
|
|
1475
1373
|
result.written.push(relPath);
|
|
1476
1374
|
return;
|
|
@@ -1478,7 +1376,7 @@ function mergeGitignore(cwd, result) {
|
|
|
1478
1376
|
assertWithinWorkspace(target, cwd, relPath);
|
|
1479
1377
|
let existing;
|
|
1480
1378
|
try {
|
|
1481
|
-
existing =
|
|
1379
|
+
existing = fs3.readFileSync(target, "utf8");
|
|
1482
1380
|
} catch (err) {
|
|
1483
1381
|
result.skipped.push(`${relPath} (could not be read, so Publisher's files are not ` + `ignored here: ${err.message})`);
|
|
1484
1382
|
return;
|
|
@@ -1494,7 +1392,7 @@ function mergeGitignore(cwd, result) {
|
|
|
1494
1392
|
` : `
|
|
1495
1393
|
|
|
1496
1394
|
`;
|
|
1497
|
-
|
|
1395
|
+
fs3.appendFileSync(target, `${prefix}${GITIGNORE_HEADER}
|
|
1498
1396
|
${missing.join(`
|
|
1499
1397
|
`)}
|
|
1500
1398
|
`);
|
|
@@ -1533,27 +1431,27 @@ var LOADABLE_DATA_EXTENSIONS = [
|
|
|
1533
1431
|
".xlsx"
|
|
1534
1432
|
];
|
|
1535
1433
|
function isLoadableDataFile(file) {
|
|
1536
|
-
return LOADABLE_DATA_EXTENSIONS.includes(
|
|
1434
|
+
return LOADABLE_DATA_EXTENSIONS.includes(path2.extname(file).toLowerCase());
|
|
1537
1435
|
}
|
|
1538
1436
|
function isSpreadsheet(dataPath) {
|
|
1539
|
-
return
|
|
1437
|
+
return path2.extname(dataPath).toLowerCase() === ".xlsx";
|
|
1540
1438
|
}
|
|
1541
1439
|
function validateDataFile(dataFile) {
|
|
1542
|
-
if (!
|
|
1440
|
+
if (!fs3.existsSync(dataFile) || !fs3.statSync(dataFile).isFile()) {
|
|
1543
1441
|
throw new ScaffoldError(`--data file not found: ${printable(dataFile)}`);
|
|
1544
1442
|
}
|
|
1545
1443
|
if (!isLoadableDataFile(dataFile)) {
|
|
1546
|
-
throw new ScaffoldError(`--data must be one of ${LOADABLE_DATA_EXTENSIONS.join(", ")} (got "${printable(
|
|
1444
|
+
throw new ScaffoldError(`--data must be one of ${LOADABLE_DATA_EXTENSIONS.join(", ")} (got "${printable(path2.basename(dataFile))}").`);
|
|
1547
1445
|
}
|
|
1548
1446
|
}
|
|
1549
1447
|
function findSiblingDataFiles(dataFile) {
|
|
1550
1448
|
let entries;
|
|
1551
1449
|
try {
|
|
1552
|
-
entries =
|
|
1450
|
+
entries = fs3.readdirSync(path2.dirname(dataFile));
|
|
1553
1451
|
} catch {
|
|
1554
1452
|
return [];
|
|
1555
1453
|
}
|
|
1556
|
-
const chosen =
|
|
1454
|
+
const chosen = path2.basename(dataFile);
|
|
1557
1455
|
return entries.filter((e) => e !== chosen && isLoadableDataFile(e)).sort().map(printable);
|
|
1558
1456
|
}
|
|
1559
1457
|
function toNpmName(base) {
|
|
@@ -1564,8 +1462,8 @@ function sanitizeFileName(base) {
|
|
|
1564
1462
|
return base.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1565
1463
|
}
|
|
1566
1464
|
function writeIfAbsent(cwd, relPath, content, options, result) {
|
|
1567
|
-
const target =
|
|
1568
|
-
const exists =
|
|
1465
|
+
const target = path2.join(cwd, relPath);
|
|
1466
|
+
const exists = fs3.existsSync(target);
|
|
1569
1467
|
if (exists && !options.force) {
|
|
1570
1468
|
result.skipped.push(relPath);
|
|
1571
1469
|
return false;
|
|
@@ -1578,20 +1476,20 @@ function writeIfAbsent(cwd, relPath, content, options, result) {
|
|
|
1578
1476
|
return true;
|
|
1579
1477
|
}
|
|
1580
1478
|
function writeFile(target, content, root) {
|
|
1581
|
-
assertWithinWorkspace(target, root,
|
|
1582
|
-
|
|
1583
|
-
|
|
1479
|
+
assertWithinWorkspace(target, root, path2.relative(root, target) || target);
|
|
1480
|
+
fs3.mkdirSync(path2.dirname(target), { recursive: true });
|
|
1481
|
+
fs3.writeFileSync(target, content);
|
|
1584
1482
|
}
|
|
1585
1483
|
function copyFile(source, target, root, label) {
|
|
1586
1484
|
assertWithinWorkspace(target, root, label);
|
|
1587
|
-
|
|
1485
|
+
fs3.copyFileSync(source, target);
|
|
1588
1486
|
}
|
|
1589
1487
|
|
|
1590
1488
|
// src/index.ts
|
|
1591
1489
|
var CLIENTS = ["claude-code", "cursor"];
|
|
1592
1490
|
function version() {
|
|
1593
1491
|
try {
|
|
1594
|
-
const pkg = JSON.parse(
|
|
1492
|
+
const pkg = JSON.parse(fs4.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
1595
1493
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
1596
1494
|
} catch {
|
|
1597
1495
|
return "unknown";
|
|
@@ -1607,7 +1505,7 @@ function resolveDataFile(cwd, data) {
|
|
|
1607
1505
|
if (data.trim() === "") {
|
|
1608
1506
|
throw new ScaffoldError(`--data was given an empty filename. Pass the CSV, Parquet, JSON, NDJSON, or XLSX ` + `file to seed the package from, or leave --data off to use the ` + `sample data.`);
|
|
1609
1507
|
}
|
|
1610
|
-
return
|
|
1508
|
+
return path3.resolve(cwd, data);
|
|
1611
1509
|
}
|
|
1612
1510
|
async function run(name, options) {
|
|
1613
1511
|
const cwd = process.cwd();
|
|
@@ -1648,11 +1546,11 @@ function artifactCandidates(name) {
|
|
|
1648
1546
|
"publisher.config.json",
|
|
1649
1547
|
"package.json",
|
|
1650
1548
|
".mcp.json",
|
|
1651
|
-
|
|
1549
|
+
path3.join(".cursor", "mcp.json"),
|
|
1652
1550
|
".gitignore",
|
|
1653
1551
|
"AGENTS.md",
|
|
1654
1552
|
"CLAUDE.md",
|
|
1655
|
-
|
|
1553
|
+
path3.join(".claude", "skills")
|
|
1656
1554
|
];
|
|
1657
1555
|
if (name !== undefined && name !== "" && name !== "." && name !== ".." && !name.includes("/") && !name.includes("\\")) {
|
|
1658
1556
|
paths.unshift(name);
|
|
@@ -1683,10 +1581,10 @@ function changesSince(cwd, name, before) {
|
|
|
1683
1581
|
return changes;
|
|
1684
1582
|
}
|
|
1685
1583
|
function fingerprint(cwd, relPath) {
|
|
1686
|
-
const target =
|
|
1584
|
+
const target = path3.join(cwd, relPath);
|
|
1687
1585
|
let stat;
|
|
1688
1586
|
try {
|
|
1689
|
-
stat =
|
|
1587
|
+
stat = fs4.lstatSync(target);
|
|
1690
1588
|
} catch {
|
|
1691
1589
|
return;
|
|
1692
1590
|
}
|
|
@@ -1695,7 +1593,7 @@ function fingerprint(cwd, relPath) {
|
|
|
1695
1593
|
return `dir:${digest(listDirectory(target))}`;
|
|
1696
1594
|
}
|
|
1697
1595
|
if (stat.isFile()) {
|
|
1698
|
-
return `file:${digest(
|
|
1596
|
+
return `file:${digest(fs4.readFileSync(target))}`;
|
|
1699
1597
|
}
|
|
1700
1598
|
return `other:${stat.mode}:${linkTarget(target)}`;
|
|
1701
1599
|
} catch {
|
|
@@ -1709,13 +1607,13 @@ function listDirectory(dir, cap = 5000) {
|
|
|
1709
1607
|
const current = stack.pop();
|
|
1710
1608
|
let children;
|
|
1711
1609
|
try {
|
|
1712
|
-
children =
|
|
1610
|
+
children = fs4.readdirSync(current, { withFileTypes: true });
|
|
1713
1611
|
} catch {
|
|
1714
|
-
entries.push(`${
|
|
1612
|
+
entries.push(`${path3.relative(dir, current)} unreadable`);
|
|
1715
1613
|
continue;
|
|
1716
1614
|
}
|
|
1717
1615
|
for (const child of children) {
|
|
1718
|
-
const full =
|
|
1616
|
+
const full = path3.join(current, child.name);
|
|
1719
1617
|
if (child.isDirectory()) {
|
|
1720
1618
|
stack.push(full);
|
|
1721
1619
|
continue;
|
|
@@ -1723,11 +1621,11 @@ function listDirectory(dir, cap = 5000) {
|
|
|
1723
1621
|
let size = -1;
|
|
1724
1622
|
let mtime = -1;
|
|
1725
1623
|
try {
|
|
1726
|
-
const childStat =
|
|
1624
|
+
const childStat = fs4.lstatSync(full);
|
|
1727
1625
|
size = childStat.size;
|
|
1728
1626
|
mtime = childStat.mtimeMs;
|
|
1729
1627
|
} catch {}
|
|
1730
|
-
entries.push(`${
|
|
1628
|
+
entries.push(`${path3.relative(dir, full)} ${size} ${mtime}`);
|
|
1731
1629
|
}
|
|
1732
1630
|
}
|
|
1733
1631
|
entries.sort();
|
|
@@ -1736,7 +1634,7 @@ function listDirectory(dir, cap = 5000) {
|
|
|
1736
1634
|
}
|
|
1737
1635
|
function linkTarget(target) {
|
|
1738
1636
|
try {
|
|
1739
|
-
return
|
|
1637
|
+
return fs4.readlinkSync(target);
|
|
1740
1638
|
} catch {
|
|
1741
1639
|
return "";
|
|
1742
1640
|
}
|
|
@@ -1815,12 +1713,12 @@ function describeFailure(err) {
|
|
|
1815
1713
|
return String(err);
|
|
1816
1714
|
}
|
|
1817
1715
|
function relativeToCwd(target) {
|
|
1818
|
-
const relative3 =
|
|
1819
|
-
return relative3 && !relative3.startsWith("..") && !
|
|
1716
|
+
const relative3 = path3.relative(process.cwd(), target);
|
|
1717
|
+
return relative3 && !relative3.startsWith("..") && !path3.isAbsolute(relative3) ? `./${relative3}` : target;
|
|
1820
1718
|
}
|
|
1821
1719
|
function readWorkspaceFile(cwd, relPath) {
|
|
1822
1720
|
try {
|
|
1823
|
-
return
|
|
1721
|
+
return fs4.readFileSync(path3.join(cwd, relPath), "utf8");
|
|
1824
1722
|
} catch {
|
|
1825
1723
|
return;
|
|
1826
1724
|
}
|
|
@@ -2160,7 +2058,7 @@ function startedAsProgram() {
|
|
|
2160
2058
|
return true;
|
|
2161
2059
|
}
|
|
2162
2060
|
try {
|
|
2163
|
-
return
|
|
2061
|
+
return fs4.realpathSync(entry) === fs4.realpathSync(fileURLToPath2(import.meta.url));
|
|
2164
2062
|
} catch {
|
|
2165
2063
|
return true;
|
|
2166
2064
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloy-publisher/create-malloy-package",
|
|
3
3
|
"description": "Scaffold a Malloy Publisher package and a local agent workspace, so one command takes you from nothing to an agent that can query your data.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.29",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
"test:e2e": "bun test --timeout 180000 ./tests/e2e"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@malloy-publisher/skills": "^0.1.
|
|
48
|
+
"@malloy-publisher/skills": "^0.1.35",
|
|
49
49
|
"commander": "^12.1.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@eslint/js": "^8.57.0",
|
|
53
|
-
"@malloydata/malloy": "^0.0.
|
|
53
|
+
"@malloydata/malloy": "^0.0.435",
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
55
55
|
"@types/bun": "^1.2.21",
|
|
56
56
|
"@types/node": "^24.10.0",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"eslint": "^8.50.0",
|
|
60
60
|
"typescript": "^5.3.3"
|
|
61
61
|
},
|
|
62
|
-
"publisherServer": "0.9.
|
|
62
|
+
"publisherServer": "0.9.3"
|
|
63
63
|
}
|