studioflow 0.1.4 → 0.1.5
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 +151 -9
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/scripts/sync-skills.mjs +60 -0
- package/skills/manifest.json +14 -0
package/dist/index.js
CHANGED
|
@@ -5898,11 +5898,15 @@ import fs11 from "node:fs/promises";
|
|
|
5898
5898
|
import path12 from "node:path";
|
|
5899
5899
|
|
|
5900
5900
|
// src/commands/install-skills.ts
|
|
5901
|
+
import { createHash } from "node:crypto";
|
|
5901
5902
|
import fs10 from "node:fs/promises";
|
|
5902
5903
|
import path11 from "node:path";
|
|
5903
5904
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5904
5905
|
var __dirname2 = path11.dirname(fileURLToPath2(import.meta.url));
|
|
5905
5906
|
var bundledSkillNames = ["studioflow-cli", "studioflow-investigate"];
|
|
5907
|
+
var skillManifestFile = "manifest.json";
|
|
5908
|
+
var skillMetadataFile = ".studioflow-skill.json";
|
|
5909
|
+
var packageName = "studioflow";
|
|
5906
5910
|
async function pathExists(target) {
|
|
5907
5911
|
try {
|
|
5908
5912
|
await fs10.access(target);
|
|
@@ -5911,6 +5915,116 @@ async function pathExists(target) {
|
|
|
5911
5915
|
return false;
|
|
5912
5916
|
}
|
|
5913
5917
|
}
|
|
5918
|
+
function toPosixPath(value) {
|
|
5919
|
+
return value.split(path11.sep).join("/");
|
|
5920
|
+
}
|
|
5921
|
+
async function listFilesRecursively(rootDir2) {
|
|
5922
|
+
const entries = await fs10.readdir(rootDir2, { withFileTypes: true });
|
|
5923
|
+
const files = [];
|
|
5924
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5925
|
+
const fullPath = path11.join(rootDir2, entry.name);
|
|
5926
|
+
if (entry.isDirectory()) {
|
|
5927
|
+
files.push(...await listFilesRecursively(fullPath));
|
|
5928
|
+
continue;
|
|
5929
|
+
}
|
|
5930
|
+
if (entry.isFile()) {
|
|
5931
|
+
files.push(fullPath);
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
return files;
|
|
5935
|
+
}
|
|
5936
|
+
async function hashDirectoryContents(rootDir2) {
|
|
5937
|
+
const hasher = createHash("sha256");
|
|
5938
|
+
const files = await listFilesRecursively(rootDir2);
|
|
5939
|
+
for (const filePath of files) {
|
|
5940
|
+
const relativePath = toPosixPath(path11.relative(rootDir2, filePath));
|
|
5941
|
+
hasher.update(relativePath);
|
|
5942
|
+
hasher.update("\n");
|
|
5943
|
+
hasher.update(await fs10.readFile(filePath));
|
|
5944
|
+
hasher.update("\n");
|
|
5945
|
+
}
|
|
5946
|
+
return hasher.digest("hex");
|
|
5947
|
+
}
|
|
5948
|
+
async function resolveCliPackageVersion(sourceDir) {
|
|
5949
|
+
const candidates = [
|
|
5950
|
+
path11.resolve(sourceDir, "../package.json"),
|
|
5951
|
+
path11.resolve(__dirname2, "../../package.json"),
|
|
5952
|
+
path11.resolve(process.cwd(), "apps/cli/package.json")
|
|
5953
|
+
];
|
|
5954
|
+
for (const candidate of candidates) {
|
|
5955
|
+
try {
|
|
5956
|
+
const raw = await fs10.readFile(candidate, "utf8");
|
|
5957
|
+
const parsed = JSON.parse(raw);
|
|
5958
|
+
if (parsed.name === packageName && typeof parsed.version === "string") {
|
|
5959
|
+
return parsed.version;
|
|
5960
|
+
}
|
|
5961
|
+
} catch {
|
|
5962
|
+
}
|
|
5963
|
+
}
|
|
5964
|
+
return "0.0.0";
|
|
5965
|
+
}
|
|
5966
|
+
function isValidManifest(value) {
|
|
5967
|
+
if (!value || typeof value !== "object") return false;
|
|
5968
|
+
const manifest = value;
|
|
5969
|
+
if (manifest.schemaVersion !== 1 || manifest.packageName !== packageName || typeof manifest.packageVersion !== "string" || !manifest.skills || typeof manifest.skills !== "object") {
|
|
5970
|
+
return false;
|
|
5971
|
+
}
|
|
5972
|
+
return bundledSkillNames.every((name) => {
|
|
5973
|
+
const entry = manifest.skills[name];
|
|
5974
|
+
return Boolean(entry && typeof entry.hash === "string" && entry.hash.length > 0);
|
|
5975
|
+
});
|
|
5976
|
+
}
|
|
5977
|
+
async function resolveBundledSkillsManifest(sourceDir) {
|
|
5978
|
+
const manifestPath = path11.join(sourceDir, skillManifestFile);
|
|
5979
|
+
if (await pathExists(manifestPath)) {
|
|
5980
|
+
try {
|
|
5981
|
+
const raw = await fs10.readFile(manifestPath, "utf8");
|
|
5982
|
+
const parsed = JSON.parse(raw);
|
|
5983
|
+
if (isValidManifest(parsed)) {
|
|
5984
|
+
return parsed;
|
|
5985
|
+
}
|
|
5986
|
+
} catch {
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
const skills = {};
|
|
5990
|
+
for (const skillName of bundledSkillNames) {
|
|
5991
|
+
const skillPath = path11.join(sourceDir, skillName);
|
|
5992
|
+
skills[skillName] = {
|
|
5993
|
+
hash: await hashDirectoryContents(skillPath)
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5996
|
+
return {
|
|
5997
|
+
schemaVersion: 1,
|
|
5998
|
+
packageName,
|
|
5999
|
+
packageVersion: await resolveCliPackageVersion(sourceDir),
|
|
6000
|
+
skills
|
|
6001
|
+
};
|
|
6002
|
+
}
|
|
6003
|
+
async function readInstalledSkillMetadata(skillDir) {
|
|
6004
|
+
const metadataPath = path11.join(skillDir, skillMetadataFile);
|
|
6005
|
+
if (!await pathExists(metadataPath)) {
|
|
6006
|
+
return null;
|
|
6007
|
+
}
|
|
6008
|
+
try {
|
|
6009
|
+
const raw = await fs10.readFile(metadataPath, "utf8");
|
|
6010
|
+
const parsed = JSON.parse(raw);
|
|
6011
|
+
if (typeof parsed.packageName === "string" && typeof parsed.cliVersion === "string" && typeof parsed.skillHash === "string" && typeof parsed.installedAt === "string") {
|
|
6012
|
+
return parsed;
|
|
6013
|
+
}
|
|
6014
|
+
} catch {
|
|
6015
|
+
}
|
|
6016
|
+
return null;
|
|
6017
|
+
}
|
|
6018
|
+
async function writeInstalledSkillMetadata(skillDir, expectedVersion, expectedHash) {
|
|
6019
|
+
const metadata = {
|
|
6020
|
+
packageName,
|
|
6021
|
+
cliVersion: expectedVersion,
|
|
6022
|
+
skillHash: expectedHash,
|
|
6023
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6024
|
+
};
|
|
6025
|
+
await fs10.writeFile(path11.join(skillDir, skillMetadataFile), `${JSON.stringify(metadata, null, 2)}
|
|
6026
|
+
`);
|
|
6027
|
+
}
|
|
5914
6028
|
async function resolveBundledSkillsDir() {
|
|
5915
6029
|
const candidates = [
|
|
5916
6030
|
process.env.STUDIOFLOW_SKILLS_SOURCE,
|
|
@@ -5934,28 +6048,48 @@ async function resolveBundledSkillsDir() {
|
|
|
5934
6048
|
`Could not locate bundled skills. Checked: ${candidates.join(", ")}. Set STUDIOFLOW_SKILLS_SOURCE if needed.`
|
|
5935
6049
|
);
|
|
5936
6050
|
}
|
|
5937
|
-
async function syncSkillsToTarget(sourceDir, targetDir, force) {
|
|
6051
|
+
async function syncSkillsToTarget(sourceDir, targetDir, force, manifest) {
|
|
5938
6052
|
await fs10.mkdir(targetDir, { recursive: true });
|
|
5939
6053
|
const installed = [];
|
|
6054
|
+
const updated = [];
|
|
5940
6055
|
const skipped = [];
|
|
5941
6056
|
for (const skillName of bundledSkillNames) {
|
|
5942
6057
|
const source = path11.join(sourceDir, skillName);
|
|
5943
6058
|
const target = path11.join(targetDir, skillName);
|
|
5944
6059
|
const exists = await pathExists(target);
|
|
5945
|
-
|
|
5946
|
-
|
|
6060
|
+
const expectedHash = manifest.skills[skillName]?.hash;
|
|
6061
|
+
if (!expectedHash) {
|
|
6062
|
+
throw new Error(`Missing skill hash in manifest for ${skillName}.`);
|
|
6063
|
+
}
|
|
6064
|
+
if (!exists) {
|
|
6065
|
+
await fs10.cp(source, target, { recursive: true });
|
|
6066
|
+
await writeInstalledSkillMetadata(target, manifest.packageVersion, expectedHash);
|
|
6067
|
+
installed.push(skillName);
|
|
5947
6068
|
continue;
|
|
5948
6069
|
}
|
|
5949
|
-
if (
|
|
6070
|
+
if (force) {
|
|
5950
6071
|
await fs10.rm(target, { recursive: true, force: true });
|
|
6072
|
+
await fs10.cp(source, target, { recursive: true });
|
|
6073
|
+
await writeInstalledSkillMetadata(target, manifest.packageVersion, expectedHash);
|
|
6074
|
+
installed.push(skillName);
|
|
6075
|
+
continue;
|
|
6076
|
+
}
|
|
6077
|
+
const currentMetadata = await readInstalledSkillMetadata(target);
|
|
6078
|
+
const matchesInstalledVersion = currentMetadata?.packageName === manifest.packageName && currentMetadata.cliVersion === manifest.packageVersion && currentMetadata.skillHash === expectedHash;
|
|
6079
|
+
if (matchesInstalledVersion) {
|
|
6080
|
+
skipped.push(skillName);
|
|
6081
|
+
continue;
|
|
5951
6082
|
}
|
|
6083
|
+
await fs10.rm(target, { recursive: true, force: true });
|
|
5952
6084
|
await fs10.cp(source, target, { recursive: true });
|
|
5953
|
-
|
|
6085
|
+
await writeInstalledSkillMetadata(target, manifest.packageVersion, expectedHash);
|
|
6086
|
+
updated.push(skillName);
|
|
5954
6087
|
}
|
|
5955
|
-
return { installed, skipped };
|
|
6088
|
+
return { installed, updated, skipped };
|
|
5956
6089
|
}
|
|
5957
6090
|
async function installBundledSkills(opts = {}) {
|
|
5958
6091
|
const sourceDir = await resolveBundledSkillsDir();
|
|
6092
|
+
const manifest = await resolveBundledSkillsManifest(sourceDir);
|
|
5959
6093
|
const force = opts.force ?? false;
|
|
5960
6094
|
const targets = [];
|
|
5961
6095
|
if (opts.targetDir && (opts.agent || opts.codexTargetDir || opts.claudeTargetDir)) {
|
|
@@ -5980,11 +6114,12 @@ async function installBundledSkills(opts = {}) {
|
|
|
5980
6114
|
}
|
|
5981
6115
|
const results = [];
|
|
5982
6116
|
for (const target of targets) {
|
|
5983
|
-
const synced = await syncSkillsToTarget(sourceDir, target.targetDir, force);
|
|
6117
|
+
const synced = await syncSkillsToTarget(sourceDir, target.targetDir, force, manifest);
|
|
5984
6118
|
results.push({
|
|
5985
6119
|
targetId: target.targetId,
|
|
5986
6120
|
targetDir: target.targetDir,
|
|
5987
6121
|
installed: synced.installed,
|
|
6122
|
+
updated: synced.updated,
|
|
5988
6123
|
skipped: synced.skipped
|
|
5989
6124
|
});
|
|
5990
6125
|
}
|
|
@@ -6001,8 +6136,11 @@ async function installSkillsCommand(opts = {}) {
|
|
|
6001
6136
|
if (target.installed.length > 0) {
|
|
6002
6137
|
console.log(`- Installed (${label}): ${target.installed.join(", ")}`);
|
|
6003
6138
|
}
|
|
6139
|
+
if (target.updated.length > 0) {
|
|
6140
|
+
console.log(`- Updated (${label}): ${target.updated.join(", ")}`);
|
|
6141
|
+
}
|
|
6004
6142
|
if (target.skipped.length > 0) {
|
|
6005
|
-
console.log(kleur_default.yellow(`- Skipped (${label},
|
|
6143
|
+
console.log(kleur_default.yellow(`- Skipped (${label}, up to date): ${target.skipped.join(", ")}`));
|
|
6006
6144
|
}
|
|
6007
6145
|
}
|
|
6008
6146
|
if (result.targets.some((target) => target.skipped.length > 0)) {
|
|
@@ -6049,8 +6187,11 @@ async function setupCommand(opts = {}) {
|
|
|
6049
6187
|
if (target.installed.length > 0) {
|
|
6050
6188
|
console.log(`- Installed (${label}): ${target.installed.join(", ")}`);
|
|
6051
6189
|
}
|
|
6190
|
+
if (target.updated.length > 0) {
|
|
6191
|
+
console.log(`- Updated (${label}): ${target.updated.join(", ")}`);
|
|
6192
|
+
}
|
|
6052
6193
|
if (target.skipped.length > 0) {
|
|
6053
|
-
console.log(kleur_default.yellow(`- Skipped (${label},
|
|
6194
|
+
console.log(kleur_default.yellow(`- Skipped (${label}, up to date): ${target.skipped.join(", ")}`));
|
|
6054
6195
|
}
|
|
6055
6196
|
}
|
|
6056
6197
|
}
|
|
@@ -6081,6 +6222,7 @@ async function setupCommand(opts = {}) {
|
|
|
6081
6222
|
targetId: target.targetId,
|
|
6082
6223
|
targetDir: target.targetDir,
|
|
6083
6224
|
installed: target.installed,
|
|
6225
|
+
updated: target.updated,
|
|
6084
6226
|
skipped: target.skipped
|
|
6085
6227
|
}))
|
|
6086
6228
|
} : { skipped: true }
|