@skaile/workspaces 0.15.1 → 0.17.0

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/asset-feeds-L4ROBIAZ.js +90 -0
  3. package/dist/asset-feeds-L4ROBIAZ.js.map +1 -0
  4. package/dist/asset-manager/index.js +5 -5
  5. package/dist/asset-manager/installer.js +4 -4
  6. package/dist/asset-manager/src/index.d.ts.map +1 -1
  7. package/dist/base-assets/connectors/deploy.js +5 -5
  8. package/dist/base-assets/connectors/devserver.js +5 -5
  9. package/dist/base-assets/connectors/flow/adapter.js +5 -5
  10. package/dist/base-assets/connectors/flow/run-flow.js +6 -6
  11. package/dist/base-assets/connectors/flow.js +5 -5
  12. package/dist/base-assets/connectors/git/driver.d.ts.map +1 -1
  13. package/dist/base-assets/connectors/git.js +5 -5
  14. package/dist/base-assets/connectors/gmail.js +5 -5
  15. package/dist/base-assets/connectors/googledrive.js +5 -5
  16. package/dist/base-assets/connectors/local.js +5 -5
  17. package/dist/base-assets/connectors/mattermost.js +5 -5
  18. package/dist/base-assets/connectors/memory.js +5 -5
  19. package/dist/base-assets/connectors/minio.js +5 -5
  20. package/dist/base-assets/connectors/postgres.js +5 -5
  21. package/dist/base-assets/connectors/redis.js +5 -5
  22. package/dist/base-assets/connectors/s3.js +5 -5
  23. package/dist/base-assets/connectors/sharepoint.js +5 -5
  24. package/dist/base-assets/connectors/sqlite.js +5 -5
  25. package/dist/base-assets/connectors/static-server.js +5 -5
  26. package/dist/base-assets/connectors/tunnel.js +5 -5
  27. package/dist/base-assets/connectors/webdav.js +5 -5
  28. package/dist/base-assets/connectors/xstate-store.js +5 -5
  29. package/dist/base-assets/connectors/xstate.js +5 -5
  30. package/dist/base-assets/connectors/yjs.js +5 -5
  31. package/dist/{chunk-SVNFQSU3.js → chunk-3DS5VIQP.js} +3 -3
  32. package/dist/{chunk-SVNFQSU3.js.map → chunk-3DS5VIQP.js.map} +1 -1
  33. package/dist/{chunk-ERCOCLW5.js → chunk-42YLNYFK.js} +4 -4
  34. package/dist/{chunk-ERCOCLW5.js.map → chunk-42YLNYFK.js.map} +1 -1
  35. package/dist/{chunk-4RUVG5GX.js → chunk-4BRSVK7Q.js} +46 -3
  36. package/dist/chunk-4BRSVK7Q.js.map +1 -0
  37. package/dist/{chunk-HSOEX3TA.js → chunk-6SCFOXJ5.js} +38 -6
  38. package/dist/chunk-6SCFOXJ5.js.map +1 -0
  39. package/dist/{chunk-G7O7WDXX.js → chunk-76CUXLXI.js} +2 -2
  40. package/dist/{chunk-G7O7WDXX.js.map → chunk-76CUXLXI.js.map} +1 -1
  41. package/dist/{chunk-DEZVZSBN.js → chunk-DG7WFRUM.js} +21 -6
  42. package/dist/chunk-DG7WFRUM.js.map +1 -0
  43. package/dist/{chunk-HJV7MHG5.js → chunk-J6T5YXLN.js} +8 -8
  44. package/dist/{chunk-HJV7MHG5.js.map → chunk-J6T5YXLN.js.map} +1 -1
  45. package/dist/{chunk-W75ASXH4.js → chunk-KJ2LLWRF.js} +3 -3
  46. package/dist/{chunk-W75ASXH4.js.map → chunk-KJ2LLWRF.js.map} +1 -1
  47. package/dist/{chunk-67TJEQJE.js → chunk-PXABRXBP.js} +5 -5
  48. package/dist/{chunk-67TJEQJE.js.map → chunk-PXABRXBP.js.map} +1 -1
  49. package/dist/{chunk-6DEGWPAR.js → chunk-SMFZFFIZ.js} +3 -3
  50. package/dist/{chunk-6DEGWPAR.js.map → chunk-SMFZFFIZ.js.map} +1 -1
  51. package/dist/{chunk-GAZINYCS.js → chunk-SOQMVRQL.js} +19 -3
  52. package/dist/chunk-SOQMVRQL.js.map +1 -0
  53. package/dist/cli/index.js +238 -117
  54. package/dist/cli/index.js.map +1 -1
  55. package/dist/cli/src/asset-feeds.d.ts +42 -0
  56. package/dist/cli/src/asset-feeds.d.ts.map +1 -0
  57. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  58. package/dist/cli/src/commands/project.d.ts.map +1 -1
  59. package/dist/cli/src/commands/source.d.ts +6 -4
  60. package/dist/cli/src/commands/source.d.ts.map +1 -1
  61. package/dist/cli/src/ensure-sources.d.ts +25 -0
  62. package/dist/cli/src/ensure-sources.d.ts.map +1 -0
  63. package/dist/connectors/config.js +4 -4
  64. package/dist/connectors/index.js +5 -5
  65. package/dist/core/index.js +3 -3
  66. package/dist/core/runtime-assets.js +2 -2
  67. package/dist/core/src/index.d.ts +1 -1
  68. package/dist/core/src/index.d.ts.map +1 -1
  69. package/dist/core/src/workspace-config.d.ts +27 -0
  70. package/dist/core/src/workspace-config.d.ts.map +1 -1
  71. package/dist/core/src/workspace-yaml-editor.d.ts +10 -1
  72. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  73. package/dist/core/workspace-config.js +1 -1
  74. package/dist/ensure-sources-V26CZNJF.js +68 -0
  75. package/dist/ensure-sources-V26CZNJF.js.map +1 -0
  76. package/dist/runner/index.js +7 -7
  77. package/dist/sdk/asset-manager.js +5 -5
  78. package/dist/sdk/core.js +3 -3
  79. package/dist/sdk/index.js +7 -7
  80. package/dist/sdk/runner.js +7 -7
  81. package/dist/{setup-WZFCLQ2J.js → setup-R6VWIPLL.js} +5 -5
  82. package/dist/{setup-WZFCLQ2J.js.map → setup-R6VWIPLL.js.map} +1 -1
  83. package/dist/{store-client-BM3IBDPT.js → store-client-FLD3XUY7.js} +6 -6
  84. package/dist/{store-client-BM3IBDPT.js.map → store-client-FLD3XUY7.js.map} +1 -1
  85. package/dist/tui/index.js +7 -7
  86. package/dist/workspace-plugin/index.js +1 -1
  87. package/package.json +1 -1
  88. package/dist/chunk-4RUVG5GX.js.map +0 -1
  89. package/dist/chunk-DEZVZSBN.js.map +0 -1
  90. package/dist/chunk-GAZINYCS.js.map +0 -1
  91. package/dist/chunk-HSOEX3TA.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import { openCatalogSource, openLibrary, createFullRegistry, openLibraryManager } from '../chunk-IY4X7PZN.js';
3
3
  import { logErr, S, logOk, colorRef, logInfo, logWarn, kindColorPad, kindColor, formatRelativeTime } from '../chunk-4NDWKA64.js';
4
- import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-6DEGWPAR.js';
4
+ import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-SMFZFFIZ.js';
5
5
  import { AI_RESOURCES } from '../chunk-2M3XTMOL.js';
6
6
  import { LocalSecretsProvider } from '../chunk-JDX54X4Y.js';
7
- import { resolveLibraryDir, skaileHomeDir } from '../chunk-FNCYNUGS.js';
7
+ import { resolveLibraryDir, LocalCatalogSource, skaileHomeDir } from '../chunk-FNCYNUGS.js';
8
8
  import '../chunk-R7FOF242.js';
9
9
  import '../chunk-GKIA2PU5.js';
10
10
  import '../chunk-OKRUTSG7.js';
11
- import { runFlow, resumeFlow } from '../chunk-67TJEQJE.js';
11
+ import { runFlow, resumeFlow } from '../chunk-PXABRXBP.js';
12
12
  import '../chunk-GCJXPUHG.js';
13
13
  import { validateFlowVersions, parseSkillFrontmatter } from '../chunk-IPUYL6TD.js';
14
- import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-HJV7MHG5.js';
15
- import { buildClaudePluginFiles } from '../chunk-G7O7WDXX.js';
14
+ import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-J6T5YXLN.js';
15
+ import { buildClaudePluginFiles } from '../chunk-76CUXLXI.js';
16
16
  import '../chunk-X5YPJV4N.js';
17
17
  import '../chunk-O7SG5PC2.js';
18
18
  import '../chunk-W2O5LWYU.js';
@@ -25,22 +25,22 @@ import '../chunk-DQWREFRQ.js';
25
25
  import '../chunk-KOVLSBXK.js';
26
26
  import '../chunk-RRVQAE5D.js';
27
27
  import '../chunk-4ACWI5YT.js';
28
- import '../chunk-HSOEX3TA.js';
29
- import '../chunk-W75ASXH4.js';
28
+ import '../chunk-6SCFOXJ5.js';
29
+ import '../chunk-KJ2LLWRF.js';
30
30
  import '../chunk-6MB7CRME.js';
31
31
  import '../chunk-QAVZOJCV.js';
32
32
  import { loadAllFlows } from '../chunk-ICS76R4T.js';
33
33
  import '../chunk-GZWJGNNN.js';
34
34
  import '../chunk-FVTV7M76.js';
35
- import { AssetManager } from '../chunk-DEZVZSBN.js';
36
- import '../chunk-ERCOCLW5.js';
37
- import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync } from '../chunk-4RUVG5GX.js';
35
+ import { AssetManager } from '../chunk-DG7WFRUM.js';
36
+ import '../chunk-42YLNYFK.js';
37
+ import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor } from '../chunk-4BRSVK7Q.js';
38
38
  import '../chunk-JKNWJ64A.js';
39
39
  import { SUPPORTED_DRIVER_TARGETS } from '../chunk-O4JH3KUE.js';
40
40
  import { DRIVER_DEFAULTS } from '../chunk-K5GBV4SA.js';
41
41
  import '../chunk-KLNL7QHN.js';
42
- import '../chunk-SVNFQSU3.js';
43
- import { resolveSkWorkspaceConfig, resolveAgentDir } from '../chunk-GAZINYCS.js';
42
+ import '../chunk-3DS5VIQP.js';
43
+ import { resolveSkWorkspaceConfig, resolveAgentDir, findWorkspaceRoot, workspaceConfigFilename } from '../chunk-SOQMVRQL.js';
44
44
  import '../chunk-ZHLRRT5D.js';
45
45
  import { ASSET_KINDS } from '../chunk-37JKX6D7.js';
46
46
  import { openSqlite } from '../chunk-24UIWON4.js';
@@ -52,7 +52,7 @@ import '../chunk-CGYEHQOX.js';
52
52
  import '../chunk-LV2HPH3C.js';
53
53
  import '../chunk-NSBPE2FW.js';
54
54
  import * as fs10 from 'fs';
55
- import fs10__default, { readFileSync, existsSync, writeFileSync, statSync } from 'fs';
55
+ import fs10__default, { readFileSync, existsSync, writeFileSync, rmSync, statSync } from 'fs';
56
56
  import * as path15 from 'path';
57
57
  import path15__default, { resolve, join } from 'path';
58
58
  import { fileURLToPath } from 'url';
@@ -308,7 +308,7 @@ function makeSearchCommand() {
308
308
  }
309
309
  if (showStore) {
310
310
  try {
311
- const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-BM3IBDPT.js');
311
+ const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-FLD3XUY7.js');
312
312
  const config = getStoreConfig2();
313
313
  const params = {};
314
314
  if (query) params.q = query;
@@ -1082,7 +1082,7 @@ function makeConnectorCommand() {
1082
1082
  cmd.command("add <driver>").option("--id <id>", "Connector instance ID (defaults to driver name)").option("--access <level>", "Access level: read-only | read-write", "read-only").option("--no-install", "Skip npm dependency installation").option("--project-dir <path>", "Workspace directory", process.cwd()).description("Add a connector to skaile.yaml and install its npm deps").action(
1083
1083
  async (driver, opts) => {
1084
1084
  const { installNpmPackages } = await import('../connectors/index.js');
1085
- const { scanDirectory, resolveBaseAssetsRoot, WorkspaceYamlEditor } = await import('../core/index.js');
1085
+ const { scanDirectory, resolveBaseAssetsRoot, WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
1086
1086
  const projectDir = path15__default.resolve(opts.projectDir);
1087
1087
  const yamlPath = path15__default.join(projectDir, "skaile.yaml");
1088
1088
  if (!existsSync(yamlPath)) {
@@ -1132,7 +1132,7 @@ ${installResult.output}`);
1132
1132
  ` \u26A0 Skipping install. Add manually: bun add --optional ${allDeps.join(" ")}`
1133
1133
  );
1134
1134
  }
1135
- const editor = WorkspaceYamlEditor.load(yamlPath);
1135
+ const editor = WorkspaceYamlEditor2.load(yamlPath);
1136
1136
  editor.setConnector({
1137
1137
  id,
1138
1138
  driver,
@@ -1146,14 +1146,14 @@ ${installResult.output}`);
1146
1146
  }
1147
1147
  );
1148
1148
  cmd.command("remove <id>").option("--project-dir <path>", "Workspace directory", process.cwd()).description("Remove a connector from skaile.yaml by its instance ID").action(async (id, opts) => {
1149
- const { WorkspaceYamlEditor } = await import('../core/index.js');
1149
+ const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
1150
1150
  const projectDir = path15__default.resolve(opts.projectDir);
1151
1151
  const yamlPath = path15__default.join(projectDir, "skaile.yaml");
1152
1152
  if (!existsSync(yamlPath)) {
1153
1153
  logErr("No skaile.yaml found in current directory.");
1154
1154
  process.exit(1);
1155
1155
  }
1156
- const editor = WorkspaceYamlEditor.load(yamlPath);
1156
+ const editor = WorkspaceYamlEditor2.load(yamlPath);
1157
1157
  const removed = editor.removeConnector(id);
1158
1158
  if (!removed) {
1159
1159
  logErr(`No connector with id '${id}' found in skaile.yaml.`);
@@ -2110,8 +2110,12 @@ function actOnHeader(row, action) {
2110
2110
  }
2111
2111
  return false;
2112
2112
  }
2113
- function loadAssets() {
2114
- const entries = am.search();
2113
+ async function loadAssets() {
2114
+ const { gatherAssetFeeds } = await import('../asset-feeds-L4ROBIAZ.js');
2115
+ const { entries, notes } = await gatherAssetFeeds(am, am.projectDir);
2116
+ if (notes.length > 0) {
2117
+ state.message = notes.map((n) => `[${n.feed}] ${n.message}`).join(" \u2022 ");
2118
+ }
2115
2119
  const installed = new Set(am.listDeployed().map((e) => `${e.kind}:${e.name}`));
2116
2120
  const bySource = /* @__PURE__ */ new Map();
2117
2121
  for (const e of entries) {
@@ -2163,7 +2167,7 @@ async function loadSourcesAndLibraries() {
2163
2167
  const { manager, library, close } = await openLibraryManager2();
2164
2168
  try {
2165
2169
  const sourcesDir2 = path15__default.join(skaileHomeDir2(), "sources");
2166
- const isSourceRow2 = (l) => l.path.startsWith(sourcesDir2);
2170
+ const isSourceRow = (l) => l.path.startsWith(sourcesDir2);
2167
2171
  const libs = await manager.listLibraries();
2168
2172
  const defs = await library.listAssetDefs();
2169
2173
  const counts = /* @__PURE__ */ new Map();
@@ -2171,13 +2175,13 @@ async function loadSourcesAndLibraries() {
2171
2175
  const id = d.libraryId ?? "";
2172
2176
  counts.set(id, (counts.get(id) ?? 0) + 1);
2173
2177
  }
2174
- state.sourceRows = libs.filter(isSourceRow2).map((l) => ({
2178
+ state.sourceRows = libs.filter(isSourceRow).map((l) => ({
2175
2179
  name: l.name,
2176
2180
  path: l.path,
2177
2181
  ownership: l.ownership,
2178
2182
  assetCount: counts.get(l.id) ?? 0
2179
2183
  }));
2180
- state.libraryRows = libs.filter((l) => !isSourceRow2(l)).map((l) => ({
2184
+ state.libraryRows = libs.filter((l) => !isSourceRow(l)).map((l) => ({
2181
2185
  name: l.name,
2182
2186
  backend: l.backend,
2183
2187
  ownership: l.ownership,
@@ -2417,7 +2421,7 @@ async function applyChanges() {
2417
2421
  }
2418
2422
  state.pendingAdds.clear();
2419
2423
  state.pendingRemoves.clear();
2420
- loadAssets();
2424
+ await loadAssets();
2421
2425
  const parts = [];
2422
2426
  if (added > 0) parts.push(pc5.green(`+${added} added`));
2423
2427
  if (removed > 0) parts.push(pc5.red(`-${removed} removed`));
@@ -2639,7 +2643,7 @@ async function handleExitConfirm(key) {
2639
2643
  }
2640
2644
  async function run(projectDir) {
2641
2645
  am = new AssetManager({ projectDir });
2642
- loadAssets();
2646
+ await loadAssets();
2643
2647
  await loadSourcesAndLibraries();
2644
2648
  hideCursor();
2645
2649
  process.stdin.setRawMode(true);
@@ -2737,9 +2741,9 @@ function makeNpxCommand() {
2737
2741
  });
2738
2742
  }
2739
2743
  spinner5.start("Syncing source");
2740
- const { LocalCatalogSource } = await import('../library/index.js');
2744
+ const { LocalCatalogSource: LocalCatalogSource2 } = await import('../library/index.js');
2741
2745
  const registry = createFullRegistry();
2742
- const catalogSource = new LocalCatalogSource(library, source.id, sourcePath, registry);
2746
+ const catalogSource = new LocalCatalogSource2(library, source.id, sourcePath, registry);
2743
2747
  const result = await catalogSource.sync();
2744
2748
  await library.syncSource(source.id);
2745
2749
  spinner5.stop(`Indexed ${result.assetsUpdated} assets`);
@@ -3666,8 +3670,23 @@ function makeInstallCommand() {
3666
3670
  }
3667
3671
  return;
3668
3672
  }
3673
+ const projectDir = path15__default.resolve(opts.projectDir);
3674
+ try {
3675
+ const { ensureSourcesCloned } = await import('../ensure-sources-V26CZNJF.js');
3676
+ const hydrate = ensureSourcesCloned(projectDir, { quiet: true });
3677
+ if (hydrate.cloned.length > 0) {
3678
+ logOk(`Cloned source(s): ${hydrate.cloned.join(", ")}`);
3679
+ }
3680
+ if (hydrate.failed.length > 0) {
3681
+ logErr(`Failed to clone source(s): ${hydrate.failed.join(", ")}`);
3682
+ }
3683
+ } catch (err) {
3684
+ logWarn(
3685
+ `source hydration skipped: ${err instanceof Error ? err.message : String(err)}`
3686
+ );
3687
+ }
3669
3688
  const am2 = new AssetManager({
3670
- projectDir: path15__default.resolve(opts.projectDir),
3689
+ projectDir,
3671
3690
  driverTarget: opts.target
3672
3691
  });
3673
3692
  const spinner5 = p5.spinner();
@@ -4036,11 +4055,11 @@ function makeClearCommand() {
4036
4055
  }
4037
4056
  function makeReplCommand() {
4038
4057
  return new Command("repl").description("Start an interactive agent session").option("--project-dir <path>", "Project directory").option("--driver <name>", "Driver backend (claude-sdk, omp)").option("--provider <name>", "LLM provider").option("--model <name>", "Model override").option("--resume", "Resume last session").option("--resume-session <id>", "Resume specific session").action(async (opts) => {
4039
- const { findWorkspaceRoot } = await import('../core/index.js');
4058
+ const { findWorkspaceRoot: findWorkspaceRoot2 } = await import('../core/index.js');
4040
4059
  const { startRepl } = await import('../tui/index.js');
4041
4060
  const os = await import('os');
4042
4061
  const userCwd = path15__default.resolve(opts.projectDir ?? process.cwd());
4043
- const workspaceRoot = findWorkspaceRoot(userCwd);
4062
+ const workspaceRoot = findWorkspaceRoot2(userCwd);
4044
4063
  let projectDir;
4045
4064
  let agentCwd;
4046
4065
  let driverOverride = opts.driver;
@@ -4116,7 +4135,7 @@ async function runCompileTest(opts) {
4116
4135
  const { tmpdir } = await import('os');
4117
4136
  const { dirname: dirname3, join: join5, resolve: resolve4 } = await import('path');
4118
4137
  const { fileURLToPath: fileURLToPath2 } = await import('url');
4119
- const { unlinkSync, existsSync: existsSync11 } = await import('fs');
4138
+ const { unlinkSync, existsSync: existsSync12 } = await import('fs');
4120
4139
  const { spawn } = await import('child_process');
4121
4140
  const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
4122
4141
  const { MONOREPO_ROOT } = await import('../paths-FKKGS6BA.js');
@@ -4178,7 +4197,7 @@ async function runCompileTest(opts) {
4178
4197
  } catch {
4179
4198
  }
4180
4199
  try {
4181
- if (existsSync11(outfile)) unlinkSync(outfile);
4200
+ if (existsSync12(outfile)) unlinkSync(outfile);
4182
4201
  } catch {
4183
4202
  }
4184
4203
  }
@@ -4948,102 +4967,193 @@ function addSourceManifestCommands(src) {
4948
4967
  function sourcesDir() {
4949
4968
  return path15.join(skaileHomeDir(), "sources");
4950
4969
  }
4970
+ function sourceClonePath(name) {
4971
+ return path15.join(sourcesDir(), name);
4972
+ }
4951
4973
  function deriveSlug(url) {
4952
4974
  return url.replace(/\.git$/, "").split(/[/:]/).pop() ?? "source";
4953
4975
  }
4954
- function isSourceRow(lib) {
4955
- return lib.path.startsWith(sourcesDir());
4976
+ function requireProjectYamlPath() {
4977
+ const root = findWorkspaceRoot(process.cwd());
4978
+ if (!root) {
4979
+ logErr(
4980
+ "No skaile.yaml found in the current directory or any parent. Run `skaile init` first."
4981
+ );
4982
+ process.exit(1);
4983
+ }
4984
+ return path15.join(root, workspaceConfigFilename());
4985
+ }
4986
+ function readProjectSources(yamlPath) {
4987
+ if (!existsSync(yamlPath)) return [];
4988
+ try {
4989
+ return WorkspaceYamlEditor.load(yamlPath).getSources();
4990
+ } catch (err) {
4991
+ logErr(`Could not read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}`);
4992
+ process.exit(1);
4993
+ }
4994
+ }
4995
+ function ensureClone(entry, opts = {}) {
4996
+ const dest = sourceClonePath(entry.name);
4997
+ if (existsSync(dest)) {
4998
+ if (!opts.force) return false;
4999
+ rmSync(dest, { recursive: true, force: true });
5000
+ }
5001
+ const args = ["clone", entry.url, dest];
5002
+ if (entry.branch) args.splice(1, 0, "--branch", entry.branch);
5003
+ const clone = spawnSync("git", args, { stdio: "inherit" });
5004
+ if (clone.status !== 0) {
5005
+ logErr(`git clone failed for ${entry.url}`);
5006
+ process.exit(1);
5007
+ }
5008
+ return true;
5009
+ }
5010
+ async function refreshManifestCache(entry) {
5011
+ const { manager, library, close } = await openLibraryManager();
5012
+ try {
5013
+ const dest = sourceClonePath(entry.name);
5014
+ const existing = (await manager.listLibraries()).find((l) => l.name === entry.name);
5015
+ const lib = existing ?? await manager.addLibrary({
5016
+ name: entry.name,
5017
+ path: dest,
5018
+ backend: "git",
5019
+ backendConfig: { url: entry.url, branch: entry.branch ?? "main", authHint: "ssh" },
5020
+ ownership: "reader"
5021
+ });
5022
+ const catalog = new LocalCatalogSource(library, lib.id, dest, createFullRegistry());
5023
+ const result = await catalog.sync();
5024
+ await library.syncSource(lib.id);
5025
+ return result;
5026
+ } finally {
5027
+ close();
5028
+ }
5029
+ }
5030
+ async function dropManifestCache(name, purgeClone) {
5031
+ const { manager, close } = await openLibraryManager();
5032
+ try {
5033
+ const existing = (await manager.listLibraries()).find((l) => l.name === name);
5034
+ if (existing) {
5035
+ await manager.removeLibrary(name, { purge: false });
5036
+ }
5037
+ } finally {
5038
+ close();
5039
+ }
5040
+ if (purgeClone) {
5041
+ const dest = sourceClonePath(name);
5042
+ if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
5043
+ }
4956
5044
  }
4957
5045
  function makeSourceCommand() {
4958
5046
  const cmd = new Command("source").description(
4959
- "Manage github sources (third-party repos of AI assets)"
5047
+ "Manage github sources for the current project (recorded in skaile.yaml)"
4960
5048
  );
4961
- cmd.command("add <git-url>").description("Clone a github repo into ~/.skaile/sources/<slug> and register it").option("--name <slug>", "Override the derived slug").action(async (url, opts) => {
5049
+ cmd.command("add <git-url>").description("Clone a github repo and register it in this project's skaile.yaml").option("--name <slug>", "Override the derived slug").option("--branch <branch>", "Branch to track (default: main)").option("--force", "Remove an existing clone and re-clone", false).action(async (url, opts) => {
5050
+ const yamlPath = requireProjectYamlPath();
4962
5051
  const slug = opts.name ?? deriveSlug(url);
4963
- const dest = path15.join(sourcesDir(), slug);
4964
- const clone = spawnSync("git", ["clone", url, dest], { stdio: "inherit" });
4965
- if (clone.status !== 0) {
4966
- logErr(`git clone failed for ${url}`);
5052
+ const entry = { name: slug, url };
5053
+ if (opts.branch) entry.branch = opts.branch;
5054
+ const editor = WorkspaceYamlEditor.load(yamlPath);
5055
+ const existing = editor.getSources().find((s) => s.name === slug);
5056
+ if (existing && existing.url !== url) {
5057
+ logErr(
5058
+ `A source named "${slug}" already exists in ${yamlPath} with a different url (${existing.url}). Pass --name to use a different slug.`
5059
+ );
4967
5060
  process.exit(1);
4968
5061
  }
4969
- const { manager, close } = await openLibraryManager();
4970
- try {
4971
- await manager.addLibrary({
4972
- name: slug,
4973
- path: dest,
4974
- backend: "git",
4975
- backendConfig: { url, branch: "main", authHint: "ssh" },
4976
- ownership: "reader"
4977
- });
4978
- logOk(`Source "${slug}" added at ${dest}.`);
4979
- } finally {
4980
- close();
5062
+ editor.setSource(entry);
5063
+ editor.save();
5064
+ const dest = sourceClonePath(slug);
5065
+ const preexisting = existsSync(dest);
5066
+ const cloned = ensureClone(entry, { force: opts.force });
5067
+ if (!cloned) {
5068
+ logOk(`Reusing cached clone at ${dest}`);
5069
+ } else if (preexisting) {
5070
+ logOk(`Re-cloned ${url} \u2192 ${dest}`);
5071
+ } else {
5072
+ logOk(`Cloned ${url} \u2192 ${dest}`);
4981
5073
  }
4982
- });
4983
- cmd.command("list").description("List registered sources").option("--json", "Output as JSON").action(async (opts) => {
4984
- const { manager, close } = await openLibraryManager();
4985
- try {
4986
- const all = await manager.listLibraries();
4987
- const sources = all.filter(isSourceRow);
4988
- if (opts.json) return console.log(JSON.stringify(sources, null, 2));
4989
- if (sources.length === 0) {
4990
- logInfo("No sources registered. Run `skaile source add <git-url>` to start.");
4991
- return;
4992
- }
4993
- console.log();
4994
- console.log(S.heading(" Sources"));
4995
- for (const s of sources) {
4996
- console.log(` ${S.cmd(s.name.padEnd(20))} ${S.dim(s.path)}`);
4997
- }
4998
- console.log(`
5074
+ const result = await refreshManifestCache(entry);
5075
+ logOk(
5076
+ `Indexed ${result.assetsUpdated} of ${result.assetsFound} asset(s) from "${slug}".`
5077
+ );
5078
+ for (const err of result.errors) logWarn(err);
5079
+ logOk(`Source "${slug}" registered in ${path15.relative(process.cwd(), yamlPath)}.`);
5080
+ });
5081
+ cmd.command("list").description("List sources registered in this project's skaile.yaml").option("--json", "Output as JSON").action(async (opts) => {
5082
+ const yamlPath = requireProjectYamlPath();
5083
+ const sources = readProjectSources(yamlPath);
5084
+ if (opts.json) return console.log(JSON.stringify(sources, null, 2));
5085
+ if (sources.length === 0) {
5086
+ logInfo("No sources in skaile.yaml. Run `skaile source add <git-url>` to register one.");
5087
+ return;
5088
+ }
5089
+ console.log();
5090
+ console.log(S.heading(" Sources"));
5091
+ for (const s of sources) {
5092
+ const cached = existsSync(sourceClonePath(s.name)) ? "" : S.warn(" (not cached)");
5093
+ console.log(` ${S.cmd(s.name.padEnd(24))} ${S.dim(s.url)}${cached}`);
5094
+ }
5095
+ console.log(`
4999
5096
  ${S.dim(`${sources.length} source(s)`)}
5000
5097
  `);
5001
- } finally {
5002
- close();
5003
- }
5004
5098
  });
5005
5099
  cmd.command("show <name>").description("Show details for a source").action(async (name) => {
5006
- const { manager, close } = await openLibraryManager();
5007
- try {
5008
- const s = await manager.requireLibrary(name);
5009
- if (!isSourceRow(s)) {
5010
- logErr(`"${name}" is a library, not a source \u2014 use \`skaile library show ${name}\`.`);
5011
- process.exit(1);
5012
- }
5013
- console.log(JSON.stringify(s, null, 2));
5014
- } finally {
5015
- close();
5100
+ const yamlPath = requireProjectYamlPath();
5101
+ const entry = readProjectSources(yamlPath).find((s) => s.name === name);
5102
+ if (!entry) {
5103
+ logErr(`No source named "${name}" in ${yamlPath}.`);
5104
+ process.exit(1);
5016
5105
  }
5106
+ console.log(
5107
+ JSON.stringify(
5108
+ {
5109
+ ...entry,
5110
+ clonePath: sourceClonePath(entry.name),
5111
+ cached: existsSync(sourceClonePath(entry.name))
5112
+ },
5113
+ null,
5114
+ 2
5115
+ )
5116
+ );
5017
5117
  });
5018
- cmd.command("remove <name>").description("Unregister a source").option("--purge", "Also delete the clone directory from disk", false).action(async (name, opts) => {
5019
- const { manager, close } = await openLibraryManager();
5020
- try {
5021
- const s = await manager.requireLibrary(name);
5022
- if (!isSourceRow(s)) {
5023
- logErr(`"${name}" is a library, not a source \u2014 use \`skaile library remove ${name}\`.`);
5024
- process.exit(1);
5025
- }
5026
- await manager.removeLibrary(name, { purge: opts.purge });
5027
- logOk(`Removed source "${name}".`);
5028
- } finally {
5029
- close();
5118
+ cmd.command("remove <name>").description("Remove a source from this project's skaile.yaml").option("--purge", "Also delete the cached clone from ~/.skaile/sources/", false).action(async (name, opts) => {
5119
+ const yamlPath = requireProjectYamlPath();
5120
+ const editor = WorkspaceYamlEditor.load(yamlPath);
5121
+ const removed = editor.removeSource(name);
5122
+ if (!removed) {
5123
+ logErr(`No source named "${name}" in ${yamlPath}.`);
5124
+ process.exit(1);
5030
5125
  }
5126
+ editor.save();
5127
+ await dropManifestCache(name, opts.purge);
5128
+ logOk(
5129
+ opts.purge ? `Removed source "${name}" and purged its clone.` : `Removed source "${name}" from skaile.yaml (clone left in ~/.skaile/sources/).`
5130
+ );
5031
5131
  });
5032
- cmd.command("sync [name]").description("Re-fetch upstream changes for one or all sources").action(async (name) => {
5033
- const { manager, close } = await openLibraryManager();
5034
- try {
5035
- const all = await manager.listLibraries();
5036
- const targets = name ? all.filter((l) => l.name === name && isSourceRow(l)) : all.filter(isSourceRow);
5037
- if (targets.length === 0) {
5038
- logInfo(name ? `No source named "${name}".` : "No sources registered.");
5039
- return;
5040
- }
5041
- for (const s of targets) {
5042
- const res = await manager.driverFor(s.backend).pull(s);
5043
- logOk(`${s.name}: pulled ${res.applied} change(s).`);
5132
+ cmd.command("sync [name]").description("Pull upstream changes and refresh the manifest cache").action(async (name) => {
5133
+ const yamlPath = requireProjectYamlPath();
5134
+ const all = readProjectSources(yamlPath);
5135
+ const targets = name ? all.filter((s) => s.name === name) : all;
5136
+ if (targets.length === 0) {
5137
+ logInfo(name ? `No source named "${name}".` : "No sources in skaile.yaml.");
5138
+ return;
5139
+ }
5140
+ for (const entry of targets) {
5141
+ const dest = sourceClonePath(entry.name);
5142
+ if (!existsSync(dest)) {
5143
+ ensureClone(entry);
5144
+ logOk(`${entry.name}: cloned to ${dest}.`);
5145
+ } else {
5146
+ const pull = spawnSync("git", ["pull", "--ff-only"], { cwd: dest, stdio: "inherit" });
5147
+ if (pull.status !== 0) {
5148
+ logWarn(`${entry.name}: git pull failed, skipping manifest refresh.`);
5149
+ continue;
5150
+ }
5044
5151
  }
5045
- } finally {
5046
- close();
5152
+ const result = await refreshManifestCache(entry);
5153
+ logOk(
5154
+ `${entry.name}: indexed ${result.assetsUpdated} of ${result.assetsFound} asset(s).`
5155
+ );
5156
+ for (const err of result.errors) logWarn(`${entry.name}: ${err}`);
5047
5157
  }
5048
5158
  });
5049
5159
  cmd.command("patch <ref>").description("Extract an asset for editing against the source").action((_ref) => {
@@ -5809,7 +5919,7 @@ program.command("init [project-dir]").description("Initialize a project director
5809
5919
  ).option("--no-git", "Skip git init and .gitignore").action(
5810
5920
  async (projectDir, opts) => {
5811
5921
  const { execSync: execSync3 } = await import('child_process');
5812
- const { existsSync: existsSync11, mkdirSync: mkdirSync3, readFileSync: readFileSync5, writeFileSync: writeFileSync2 } = await import('fs');
5922
+ const { existsSync: existsSync12, mkdirSync: mkdirSync3, readFileSync: readFileSync5, writeFileSync: writeFileSync2 } = await import('fs');
5813
5923
  const { stringify } = await import('yaml');
5814
5924
  const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
5815
5925
  const backend = opts.backend;
@@ -5823,22 +5933,22 @@ program.command("init [project-dir]").description("Initialize a project director
5823
5933
  const resolved = path15__default.resolve(projectDir ?? ".");
5824
5934
  const projectName = path15__default.basename(resolved);
5825
5935
  const created = [];
5826
- if (!existsSync11(resolved)) {
5936
+ if (!existsSync12(resolved)) {
5827
5937
  mkdirSync3(resolved, { recursive: true });
5828
5938
  created.push(".");
5829
5939
  }
5830
5940
  const skaileDir = path15__default.join(resolved, ".skaile");
5831
- if (!existsSync11(skaileDir)) {
5941
+ if (!existsSync12(skaileDir)) {
5832
5942
  mkdirSync3(path15__default.join(skaileDir, "sessions"), { recursive: true });
5833
5943
  created.push(".skaile/");
5834
5944
  }
5835
5945
  const settingsPath = path15__default.join(skaileDir, "settings.json");
5836
- if (!existsSync11(settingsPath)) {
5946
+ if (!existsSync12(settingsPath)) {
5837
5947
  writeFileSync2(settingsPath, "{}\n");
5838
5948
  created.push(".skaile/settings.json");
5839
5949
  }
5840
5950
  const wsConfigPath = path15__default.join(resolved, "skaile.yaml");
5841
- if (!existsSync11(wsConfigPath)) {
5951
+ if (!existsSync12(wsConfigPath)) {
5842
5952
  writeFileSync2(
5843
5953
  wsConfigPath,
5844
5954
  stringify({
@@ -5860,7 +5970,7 @@ program.command("init [project-dir]").description("Initialize a project director
5860
5970
  }
5861
5971
  for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
5862
5972
  const full = path15__default.join(resolved, dir);
5863
- if (!existsSync11(full)) {
5973
+ if (!existsSync12(full)) {
5864
5974
  mkdirSync3(full, { recursive: true });
5865
5975
  created.push(`${dir}/`);
5866
5976
  }
@@ -5868,7 +5978,7 @@ program.command("init [project-dir]").description("Initialize a project director
5868
5978
  if (opts.git) {
5869
5979
  const gitignorePath = path15__default.join(resolved, ".gitignore");
5870
5980
  const entries = ["node_modules/", ".skaile/sessions/", "*.log", ".env", ".env.local"];
5871
- const existing = existsSync11(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : "";
5981
+ const existing = existsSync12(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : "";
5872
5982
  const have = new Set(existing.split("\n").map((l) => l.trim()));
5873
5983
  const append = entries.filter((e) => !have.has(e));
5874
5984
  if (append.length > 0) {
@@ -5877,7 +5987,7 @@ program.command("init [project-dir]").description("Initialize a project director
5877
5987
  `);
5878
5988
  if (!existing) created.push(".gitignore");
5879
5989
  }
5880
- if (!existsSync11(path15__default.join(resolved, ".git"))) {
5990
+ if (!existsSync12(path15__default.join(resolved, ".git"))) {
5881
5991
  try {
5882
5992
  execSync3("git init", { cwd: resolved, stdio: "pipe" });
5883
5993
  created.push(".git/");
@@ -5903,6 +6013,17 @@ program.command("init [project-dir]").description("Initialize a project director
5903
6013
  ` ${S.dim(`\u26A0 could not bootstrap user config: ${err instanceof Error ? err.message : String(err)}`)}`
5904
6014
  );
5905
6015
  }
6016
+ try {
6017
+ const { ensureSourcesCloned } = await import('../ensure-sources-V26CZNJF.js');
6018
+ const hydrate = ensureSourcesCloned(resolved, { quiet: true });
6019
+ for (const n of hydrate.cloned) created.push(`~/.skaile/sources/${n}/`);
6020
+ if (hydrate.failed.length > 0) {
6021
+ logErr(`Failed to clone source(s): ${hydrate.failed.join(", ")}`);
6022
+ }
6023
+ } catch (err) {
6024
+ const msg = err instanceof Error ? err.message : String(err);
6025
+ console.log(` ${S.dim(`\u26A0 source hydration skipped: ${msg}`)}`);
6026
+ }
5906
6027
  for (const c of created) console.log(` ${S.dim(`+ ${c}`)}`);
5907
6028
  logOk(`Initialized project at ${S.heading(resolved)}`);
5908
6029
  console.log();
@@ -5920,7 +6041,7 @@ program.command("init [project-dir]").description("Initialize a project director
5920
6041
  }
5921
6042
  );
5922
6043
  program.command("setup").description("Interactive provider setup wizard").action(async () => {
5923
- const { cmdSetup } = await import('../setup-WZFCLQ2J.js');
6044
+ const { cmdSetup } = await import('../setup-R6VWIPLL.js');
5924
6045
  await cmdSetup([], { projectDir: process.cwd() });
5925
6046
  });
5926
6047
  program.addCommand(makeInstallCommand());