@skaile/workspaces 0.15.0 → 0.16.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 (102) hide show
  1. package/CHANGELOG.md +53 -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/asset-manager/src/installer.d.ts.map +1 -1
  8. package/dist/base-assets/connectors/deploy.js +5 -5
  9. package/dist/base-assets/connectors/devserver.js +5 -5
  10. package/dist/base-assets/connectors/flow/adapter.js +5 -5
  11. package/dist/base-assets/connectors/flow/run-flow.js +6 -6
  12. package/dist/base-assets/connectors/flow.js +5 -5
  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-4GEVGRWB.js → chunk-42YLNYFK.js} +11 -13
  34. package/dist/chunk-42YLNYFK.js.map +1 -0
  35. package/dist/{chunk-4RUVG5GX.js → chunk-4BRSVK7Q.js} +46 -3
  36. package/dist/chunk-4BRSVK7Q.js.map +1 -0
  37. package/dist/{chunk-Z24KPZKU.js → chunk-75M5W7FX.js} +8 -8
  38. package/dist/{chunk-Z24KPZKU.js.map → chunk-75M5W7FX.js.map} +1 -1
  39. package/dist/{chunk-SO43XRWF.js → chunk-ALJM24WL.js} +5 -5
  40. package/dist/{chunk-SO43XRWF.js.map → chunk-ALJM24WL.js.map} +1 -1
  41. package/dist/{chunk-2NIOMFSQ.js → chunk-FNCYNUGS.js} +78 -81
  42. package/dist/chunk-FNCYNUGS.js.map +1 -0
  43. package/dist/{chunk-6EN5IJ2Y.js → chunk-IY4X7PZN.js} +3 -3
  44. package/dist/{chunk-6EN5IJ2Y.js.map → chunk-IY4X7PZN.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-6DEGWPAR.js → chunk-SMFZFFIZ.js} +3 -3
  48. package/dist/{chunk-6DEGWPAR.js.map → chunk-SMFZFFIZ.js.map} +1 -1
  49. package/dist/{chunk-GAZINYCS.js → chunk-SOQMVRQL.js} +19 -3
  50. package/dist/chunk-SOQMVRQL.js.map +1 -0
  51. package/dist/{chunk-7R4WLTZW.js → chunk-YMMWP3YL.js} +29 -19
  52. package/dist/chunk-YMMWP3YL.js.map +1 -0
  53. package/dist/{chunk-HSOEX3TA.js → chunk-YW36VEVN.js} +4 -4
  54. package/dist/{chunk-HSOEX3TA.js.map → chunk-YW36VEVN.js.map} +1 -1
  55. package/dist/{chunk-G7O7WDXX.js → chunk-ZUQXHBEH.js} +2 -2
  56. package/dist/{chunk-G7O7WDXX.js.map → chunk-ZUQXHBEH.js.map} +1 -1
  57. package/dist/cli/index.js +376 -207
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/src/asset-feeds.d.ts +42 -0
  60. package/dist/cli/src/asset-feeds.d.ts.map +1 -0
  61. package/dist/cli/src/commands/manage.d.ts +31 -20
  62. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  63. package/dist/cli/src/commands/project.d.ts.map +1 -1
  64. package/dist/cli/src/commands/source.d.ts +6 -4
  65. package/dist/cli/src/commands/source.d.ts.map +1 -1
  66. package/dist/cli/src/ensure-sources.d.ts +25 -0
  67. package/dist/cli/src/ensure-sources.d.ts.map +1 -0
  68. package/dist/connectors/config.js +4 -4
  69. package/dist/connectors/index.js +5 -5
  70. package/dist/core/index.js +3 -3
  71. package/dist/core/runtime-assets.js +2 -2
  72. package/dist/core/src/index.d.ts +1 -1
  73. package/dist/core/src/index.d.ts.map +1 -1
  74. package/dist/core/src/workspace-config.d.ts +27 -0
  75. package/dist/core/src/workspace-config.d.ts.map +1 -1
  76. package/dist/core/src/workspace-yaml-editor.d.ts +10 -1
  77. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  78. package/dist/core/workspace-config.js +1 -1
  79. package/dist/ensure-sources-V26CZNJF.js +68 -0
  80. package/dist/ensure-sources-V26CZNJF.js.map +1 -0
  81. package/dist/library/index.js +1 -1
  82. package/dist/library/src/local/db.d.ts.map +1 -1
  83. package/dist/library/src/local/store-paths.d.ts.map +1 -1
  84. package/dist/{open-library-XD7QYLMW.js → open-library-T6RXQJTQ.js} +4 -4
  85. package/dist/{open-library-XD7QYLMW.js.map → open-library-T6RXQJTQ.js.map} +1 -1
  86. package/dist/runner/index.js +7 -7
  87. package/dist/sdk/asset-manager.js +5 -5
  88. package/dist/sdk/core.js +3 -3
  89. package/dist/sdk/index.js +7 -7
  90. package/dist/sdk/runner.js +7 -7
  91. package/dist/{setup-WZFCLQ2J.js → setup-R6VWIPLL.js} +5 -5
  92. package/dist/{setup-WZFCLQ2J.js.map → setup-R6VWIPLL.js.map} +1 -1
  93. package/dist/{store-client-BM3IBDPT.js → store-client-FLD3XUY7.js} +6 -6
  94. package/dist/{store-client-BM3IBDPT.js.map → store-client-FLD3XUY7.js.map} +1 -1
  95. package/dist/tui/index.js +7 -7
  96. package/dist/workspace-plugin/index.js +1 -1
  97. package/package.json +1 -1
  98. package/dist/chunk-2NIOMFSQ.js.map +0 -1
  99. package/dist/chunk-4GEVGRWB.js.map +0 -1
  100. package/dist/chunk-4RUVG5GX.js.map +0 -1
  101. package/dist/chunk-7R4WLTZW.js.map +0 -1
  102. package/dist/chunk-GAZINYCS.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { openCatalogSource, openLibrary, createFullRegistry, openLibraryManager } from '../chunk-6EN5IJ2Y.js';
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-2NIOMFSQ.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-SO43XRWF.js';
11
+ import { runFlow, resumeFlow } from '../chunk-ALJM24WL.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-Z24KPZKU.js';
15
- import { buildClaudePluginFiles } from '../chunk-G7O7WDXX.js';
14
+ import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-75M5W7FX.js';
15
+ import { buildClaudePluginFiles } from '../chunk-ZUQXHBEH.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-YW36VEVN.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-7R4WLTZW.js';
36
- import '../chunk-4GEVGRWB.js';
37
- import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync } from '../chunk-4RUVG5GX.js';
35
+ import { AssetManager } from '../chunk-YMMWP3YL.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;
@@ -470,7 +470,7 @@ function makeCatalogCommand() {
470
470
  const cfg = resolveConfig({ projectDir });
471
471
  const baseUrl = opts.url ?? cfg.catalog.url;
472
472
  if (isLocalCatalogUrl(baseUrl)) {
473
- const { resolveCatalogSource } = await import('../open-library-XD7QYLMW.js');
473
+ const { resolveCatalogSource } = await import('../open-library-T6RXQJTQ.js');
474
474
  let resolved;
475
475
  try {
476
476
  resolved = await resolveCatalogSource({ projectDir: opts.projectDir });
@@ -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.`);
@@ -2022,42 +2022,43 @@ function makeLogsCommand() {
2022
2022
  }
2023
2023
  var am;
2024
2024
  var state = {
2025
- tab: "libraries",
2025
+ tab: "assets",
2026
2026
  cursor: 0,
2027
2027
  assetRows: [],
2028
+ sourceRows: [],
2028
2029
  libraryRows: [],
2029
2030
  visibleRows: [],
2030
2031
  pendingAdds: /* @__PURE__ */ new Set(),
2031
2032
  pendingRemoves: /* @__PURE__ */ new Set(),
2032
- collapsedRepos: /* @__PURE__ */ new Set(),
2033
+ collapsedSources: /* @__PURE__ */ new Set(),
2033
2034
  collapsedDomains: /* @__PURE__ */ new Set(),
2034
2035
  message: "",
2035
2036
  cols: 80,
2036
2037
  awaitingExitConfirm: false
2037
2038
  };
2038
- function buildVisibleRows(rows, collapsedRepos, collapsedDomains) {
2039
+ function buildVisibleRows(rows, collapsedSources, collapsedDomains) {
2039
2040
  return rows.filter((row) => {
2040
- if (row.type === "repo-header") return true;
2041
- if (row.type === "header") return !collapsedRepos.has(row.repo);
2042
- const repo = row.entry?.repository ?? "other";
2041
+ if (row.type === "source-header") return true;
2042
+ if (row.type === "header") return !collapsedSources.has(row.source);
2043
+ const source = row.entry?.repository ?? "other";
2043
2044
  const domain = row.entry?.domain ?? "other";
2044
- return !collapsedRepos.has(repo) && !collapsedDomains.has(`${repo}:${domain}`);
2045
+ return !collapsedSources.has(source) && !collapsedDomains.has(`${source}:${domain}`);
2045
2046
  });
2046
2047
  }
2047
- function domainAssetRefs(rows, repo, domain) {
2048
- return rows.filter((r) => isAsset(r) && inDomain(r, repo, domain)).map((r) => assetRefOf(r));
2048
+ function domainAssetRefs(rows, source, domain) {
2049
+ return rows.filter((r) => isAsset(r) && inDomain(r, source, domain)).map((r) => assetRefOf(r));
2049
2050
  }
2050
- function repoAssetRefs(rows, repo) {
2051
- return rows.filter((r) => isAsset(r) && inRepo(r, repo)).map((r) => assetRefOf(r));
2051
+ function sourceAssetRefs(rows, source) {
2052
+ return rows.filter((r) => isAsset(r) && inSource(r, source)).map((r) => assetRefOf(r));
2052
2053
  }
2053
2054
  function isAsset(r) {
2054
2055
  return r.type === "asset";
2055
2056
  }
2056
- function inRepo(r, repo) {
2057
- return (r.entry?.repository ?? "other") === repo;
2057
+ function inSource(r, source) {
2058
+ return (r.entry?.repository ?? "other") === source;
2058
2059
  }
2059
- function inDomain(r, repo, domain) {
2060
- return inRepo(r, repo) && (r.entry?.domain ?? "other") === domain;
2060
+ function inDomain(r, source, domain) {
2061
+ return inSource(r, source) && (r.entry?.domain ?? "other") === domain;
2061
2062
  }
2062
2063
  function assetRefOf(r) {
2063
2064
  if (!isAsset(r) || !r.entry) return null;
@@ -2081,17 +2082,17 @@ function setSelection(ref, action) {
2081
2082
  state.pendingRemoves.delete(ref);
2082
2083
  return;
2083
2084
  }
2084
- resolved = row.deployed ? "remove" : "add";
2085
+ resolved = row.installed ? "remove" : "add";
2085
2086
  } else {
2086
2087
  resolved = action;
2087
2088
  }
2088
2089
  if (resolved === "add") {
2089
2090
  state.pendingRemoves.delete(ref);
2090
- if (row.deployed) return;
2091
+ if (row.installed) return;
2091
2092
  state.pendingAdds.add(ref);
2092
2093
  } else {
2093
2094
  state.pendingAdds.delete(ref);
2094
- if (!row.deployed) return;
2095
+ if (!row.installed) return;
2095
2096
  state.pendingRemoves.add(ref);
2096
2097
  }
2097
2098
  }
@@ -2099,51 +2100,57 @@ function bulkSelection(refs, action) {
2099
2100
  for (const ref of refs) setSelection(ref, action);
2100
2101
  }
2101
2102
  function actOnHeader(row, action) {
2102
- if (row.type === "repo-header") {
2103
- bulkSelection(repoAssetRefs(state.assetRows, row.repo), action);
2103
+ if (row.type === "source-header") {
2104
+ bulkSelection(sourceAssetRefs(state.assetRows, row.source), action);
2104
2105
  return true;
2105
2106
  }
2106
2107
  if (row.type === "header") {
2107
- bulkSelection(domainAssetRefs(state.assetRows, row.repo, row.domain), action);
2108
+ bulkSelection(domainAssetRefs(state.assetRows, row.source, row.domain), action);
2108
2109
  return true;
2109
2110
  }
2110
2111
  return false;
2111
2112
  }
2112
- function loadAssets() {
2113
- const entries = am.search();
2114
- const deployed = new Set(am.listDeployed().map((e) => `${e.kind}:${e.name}`));
2115
- const byRepo = /* @__PURE__ */ new Map();
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
+ }
2119
+ const installed = new Set(am.listDeployed().map((e) => `${e.kind}:${e.name}`));
2120
+ const bySource = /* @__PURE__ */ new Map();
2116
2121
  for (const e of entries) {
2117
- const repo = e.repository ?? "other";
2122
+ const source = e.repository ?? "other";
2118
2123
  const domain = e.domain ?? "other";
2119
- if (!byRepo.has(repo)) byRepo.set(repo, /* @__PURE__ */ new Map());
2120
- const domainMap = byRepo.get(repo);
2124
+ if (!bySource.has(source)) bySource.set(source, /* @__PURE__ */ new Map());
2125
+ const domainMap = bySource.get(source);
2121
2126
  if (!domainMap.has(domain)) domainMap.set(domain, []);
2122
2127
  domainMap.get(domain).push(e);
2123
2128
  }
2124
2129
  const rows = [];
2125
- for (const [repo, domainMap] of [...byRepo.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
2126
- rows.push({ type: "repo-header", repo });
2130
+ for (const [source, domainMap] of [...bySource.entries()].sort(
2131
+ (a, b) => a[0].localeCompare(b[0])
2132
+ )) {
2133
+ rows.push({ type: "source-header", source });
2127
2134
  for (const [domain, items] of [...domainMap.entries()].sort(
2128
2135
  (a, b) => a[0].localeCompare(b[0])
2129
2136
  )) {
2130
- rows.push({ type: "header", repo, domain });
2137
+ rows.push({ type: "header", source, domain });
2131
2138
  items.sort(
2132
2139
  (a, b) => a.kind !== b.kind ? a.kind.localeCompare(b.kind) : a.name.localeCompare(b.name)
2133
2140
  );
2134
2141
  for (const entry of items) {
2135
2142
  const ref = `${entry.kind}:${entry.name}`;
2136
- rows.push({ type: "asset", entry, deployed: deployed.has(ref) });
2143
+ rows.push({ type: "asset", entry, installed: installed.has(ref) });
2137
2144
  }
2138
2145
  }
2139
2146
  }
2140
2147
  state.assetRows = rows;
2141
- const newRepos = new Set(rows.filter((r) => r.type === "repo-header").map((r) => r.repo));
2142
- for (const r of state.collapsedRepos) {
2143
- if (!newRepos.has(r)) state.collapsedRepos.delete(r);
2148
+ const newSources = new Set(rows.filter((r) => r.type === "source-header").map((r) => r.source));
2149
+ for (const s of state.collapsedSources) {
2150
+ if (!newSources.has(s)) state.collapsedSources.delete(s);
2144
2151
  }
2145
2152
  const newDomainKeys = new Set(
2146
- rows.filter((r) => r.type === "header").map((r) => `${r.repo}:${r.domain}`)
2153
+ rows.filter((r) => r.type === "header").map((r) => `${r.source}:${r.domain}`)
2147
2154
  );
2148
2155
  for (const d of state.collapsedDomains) {
2149
2156
  if (!newDomainKeys.has(d)) state.collapsedDomains.delete(d);
@@ -2151,11 +2158,16 @@ function loadAssets() {
2151
2158
  for (const d of newDomainKeys) state.collapsedDomains.add(d);
2152
2159
  rebuildVisible();
2153
2160
  }
2154
- async function loadLibraries() {
2161
+ async function loadSourcesAndLibraries() {
2155
2162
  try {
2156
- const { openLibraryManager: openLibraryManager2 } = await import('../open-library-XD7QYLMW.js');
2163
+ const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir: skaileHomeDir2 }] = await Promise.all([
2164
+ import('../open-library-T6RXQJTQ.js'),
2165
+ import('../library/index.js')
2166
+ ]);
2157
2167
  const { manager, library, close } = await openLibraryManager2();
2158
2168
  try {
2169
+ const sourcesDir2 = path15__default.join(skaileHomeDir2(), "sources");
2170
+ const isSourceRow = (l) => l.path.startsWith(sourcesDir2);
2159
2171
  const libs = await manager.listLibraries();
2160
2172
  const defs = await library.listAssetDefs();
2161
2173
  const counts = /* @__PURE__ */ new Map();
@@ -2163,7 +2175,13 @@ async function loadLibraries() {
2163
2175
  const id = d.libraryId ?? "";
2164
2176
  counts.set(id, (counts.get(id) ?? 0) + 1);
2165
2177
  }
2166
- state.libraryRows = libs.map((l) => ({
2178
+ state.sourceRows = libs.filter(isSourceRow).map((l) => ({
2179
+ name: l.name,
2180
+ path: l.path,
2181
+ ownership: l.ownership,
2182
+ assetCount: counts.get(l.id) ?? 0
2183
+ }));
2184
+ state.libraryRows = libs.filter((l) => !isSourceRow(l)).map((l) => ({
2167
2185
  name: l.name,
2168
2186
  backend: l.backend,
2169
2187
  ownership: l.ownership,
@@ -2174,13 +2192,14 @@ async function loadLibraries() {
2174
2192
  close();
2175
2193
  }
2176
2194
  } catch {
2195
+ state.sourceRows = [];
2177
2196
  state.libraryRows = [];
2178
2197
  }
2179
2198
  }
2180
2199
  function rebuildVisible() {
2181
2200
  state.visibleRows = buildVisibleRows(
2182
2201
  state.assetRows,
2183
- state.collapsedRepos,
2202
+ state.collapsedSources,
2184
2203
  state.collapsedDomains
2185
2204
  );
2186
2205
  clampCursor();
@@ -2195,19 +2214,11 @@ function moveCursor(delta) {
2195
2214
  state.cursor = Math.max(0, Math.min(maxCursor(), state.cursor + delta));
2196
2215
  }
2197
2216
  function nextTab() {
2198
- const order = ["libraries", "assets", "pending", "sync"];
2217
+ const order = ["assets", "sources", "libraries", "pending", "sync"];
2199
2218
  state.tab = order[(order.indexOf(state.tab) + 1) % order.length];
2200
2219
  state.cursor = 0;
2201
2220
  }
2202
2221
  var TABS = {
2203
- libraries: {
2204
- label: "Libraries",
2205
- rowCount: () => state.libraryRows.length,
2206
- selectableCount: () => state.libraryRows.length,
2207
- emptyMsg: " No libraries registered. Run `skaile library init <name>` or `skaile library add <git-url>` from the shell.",
2208
- renderRow: (i, sel) => renderLibraryRow(state.libraryRows[i], sel),
2209
- footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile library \u2026` from the shell for CRUD)"
2210
- },
2211
2222
  assets: {
2212
2223
  label: "Assets",
2213
2224
  rowCount: () => state.visibleRows.length,
@@ -2215,6 +2226,22 @@ var TABS = {
2215
2226
  renderRow: (i, sel) => renderAssetRow(state.visibleRows[i], sel),
2216
2227
  footer: " \u2191\u2193/jk navigate \u2190/\u2192 collapse/expand [space/+/-] select [a/\u21B5] apply [i] info Tab switch [q/esc] quit"
2217
2228
  },
2229
+ sources: {
2230
+ label: "Sources",
2231
+ rowCount: () => state.sourceRows.length,
2232
+ selectableCount: () => state.sourceRows.length,
2233
+ emptyMsg: " No sources registered. Run `skaile source add <git-url>` from the shell to track a github repo.",
2234
+ renderRow: (i, sel) => renderSourceRow(state.sourceRows[i], sel),
2235
+ footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile source \u2026` from the shell for CRUD)"
2236
+ },
2237
+ libraries: {
2238
+ label: "Libraries",
2239
+ rowCount: () => state.libraryRows.length,
2240
+ selectableCount: () => state.libraryRows.length,
2241
+ emptyMsg: " No libraries registered. Run `skaile library init <name>` from the shell to author your own.",
2242
+ renderRow: (i, sel) => renderLibraryRow(state.libraryRows[i], sel),
2243
+ footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile library \u2026` from the shell for CRUD)"
2244
+ },
2218
2245
  pending: {
2219
2246
  label: "Pending",
2220
2247
  rowCount: () => 0,
@@ -2227,7 +2254,7 @@ var TABS = {
2227
2254
  label: "Sync",
2228
2255
  rowCount: () => 0,
2229
2256
  selectableCount: () => 0,
2230
- emptyMsg: " Press S to sync all libraries (rich rendering deferred to AF-LIB-TUI-RICH).",
2257
+ emptyMsg: " Press S to sync all sources + libraries (rich rendering deferred to AF-LIB-TUI-RICH).",
2231
2258
  renderRow: () => "",
2232
2259
  footer: " s sync all Tab switch q quit"
2233
2260
  }
@@ -2264,13 +2291,13 @@ function groupStatus(refs) {
2264
2291
  if (refs.length === 0) return pc5.dim("\xB7 ");
2265
2292
  if (refs.some((r) => state.pendingAdds.has(r))) return pc5.blue("+ ");
2266
2293
  if (refs.some((r) => state.pendingRemoves.has(r))) return pc5.red("- ");
2267
- const allDeployed = refs.every((r) => findAssetRow(r)?.deployed);
2268
- return allDeployed ? pc5.green("\u2713 ") : pc5.dim("\xB7 ");
2294
+ const allInstalled = refs.every((r) => findAssetRow(r)?.installed);
2295
+ return allInstalled ? pc5.green("\u2713 ") : pc5.dim("\xB7 ");
2269
2296
  }
2270
2297
  function renderHeaderLine(opts) {
2271
2298
  const indicator = opts.collapsed ? "\u25B6" : "\u25BC";
2272
2299
  const total = state.assetRows.filter(opts.match).length;
2273
- const installed = state.assetRows.filter((r) => opts.match(r) && r.deployed).length;
2300
+ const installed = state.assetRows.filter((r) => opts.match(r) && r.installed).length;
2274
2301
  const [add, remove] = pendingCountsFor(opts.match);
2275
2302
  let count = String(installed);
2276
2303
  if (add > 0) count += pc5.green(`+${add}`);
@@ -2281,22 +2308,22 @@ function renderHeaderLine(opts) {
2281
2308
  return opts.selected ? pc5.bgWhite(pc5.black(`${line} `.padEnd(state.cols))) : line;
2282
2309
  }
2283
2310
  function renderAssetRow(row, selected) {
2284
- if (row.type === "repo-header") {
2311
+ if (row.type === "source-header") {
2285
2312
  return renderHeaderLine({
2286
2313
  indent: " ",
2287
- collapsed: state.collapsedRepos.has(row.repo),
2288
- match: (r) => isAsset(r) && inRepo(r, row.repo),
2289
- refs: repoAssetRefs(state.assetRows, row.repo),
2290
- label: pc5.bold(pc5.cyan(row.repo)),
2314
+ collapsed: state.collapsedSources.has(row.source),
2315
+ match: (r) => isAsset(r) && inSource(r, row.source),
2316
+ refs: sourceAssetRefs(state.assetRows, row.source),
2317
+ label: pc5.bold(pc5.cyan(row.source)),
2291
2318
  selected
2292
2319
  });
2293
2320
  }
2294
2321
  if (row.type === "header") {
2295
2322
  return renderHeaderLine({
2296
2323
  indent: " ",
2297
- collapsed: state.collapsedDomains.has(`${row.repo}:${row.domain}`),
2298
- match: (r) => isAsset(r) && inDomain(r, row.repo, row.domain),
2299
- refs: domainAssetRefs(state.assetRows, row.repo, row.domain),
2324
+ collapsed: state.collapsedDomains.has(`${row.source}:${row.domain}`),
2325
+ match: (r) => isAsset(r) && inDomain(r, row.source, row.domain),
2326
+ refs: domainAssetRefs(state.assetRows, row.source, row.domain),
2300
2327
  label: pc5.bold(row.domain),
2301
2328
  selected
2302
2329
  });
@@ -2306,7 +2333,7 @@ function renderAssetRow(row, selected) {
2306
2333
  let status4;
2307
2334
  if (state.pendingAdds.has(ref)) status4 = pc5.blue("+ ");
2308
2335
  else if (state.pendingRemoves.has(ref)) status4 = pc5.red("- ");
2309
- else if (row.deployed) status4 = pc5.green("\u2713 ");
2336
+ else if (row.installed) status4 = pc5.green("\u2713 ");
2310
2337
  else status4 = pc5.dim("\xB7 ");
2311
2338
  const kind = kindColorPad(e.kind, 8);
2312
2339
  const name = e.name.padEnd(30);
@@ -2314,6 +2341,10 @@ function renderAssetRow(row, selected) {
2314
2341
  const line = ` ${status4} ${kind} ${name} ${pc5.dim(desc)}`;
2315
2342
  return selected ? pc5.inverse(line.padEnd(state.cols)) : line;
2316
2343
  }
2344
+ function renderSourceRow(row, selected) {
2345
+ const line = ` ${S.cmd(row.name.padEnd(20))} ${pc5.dim(row.ownership.padEnd(12))} ${pc5.dim(`${row.assetCount} assets`)} ${pc5.dim(row.path)}`;
2346
+ return selected ? pc5.inverse(line.padEnd(state.cols)) : line;
2347
+ }
2317
2348
  function renderLibraryRow(row, selected) {
2318
2349
  const mark = row.isDefault ? pc5.green("\u25B8") : " ";
2319
2350
  const line = `${mark} ${S.cmd(row.name.padEnd(16))} ${pc5.dim(row.backend.padEnd(7))} ${pc5.dim(row.ownership.padEnd(12))} ${pc5.dim(`${row.assetCount} assets`)}`;
@@ -2390,20 +2421,25 @@ async function applyChanges() {
2390
2421
  }
2391
2422
  state.pendingAdds.clear();
2392
2423
  state.pendingRemoves.clear();
2393
- loadAssets();
2424
+ await loadAssets();
2394
2425
  const parts = [];
2395
2426
  if (added > 0) parts.push(pc5.green(`+${added} added`));
2396
2427
  if (removed > 0) parts.push(pc5.red(`-${removed} removed`));
2397
2428
  if (parts.length) state.message = parts.join(" ");
2398
2429
  }
2399
- async function syncLibraries() {
2430
+ async function syncAll() {
2400
2431
  state.message = pc5.yellow(
2401
- "Library sync is not yet driven from the manage TUI. Run `skaile library sync <name>` from the shell."
2432
+ "Sync is not yet driven from the manage TUI. Run `skaile source sync` (sources) or `skaile library sync <name>` (git-backed libraries) from the shell."
2402
2433
  );
2403
2434
  }
2404
- function shellHint(action) {
2435
+ function sourceShellHint(action) {
2405
2436
  state.message = pc5.yellow(
2406
- `${action} is a shell command in the Libraries redesign. Run \`skaile library \u2026\` from the shell.`
2437
+ `${action} is a shell command. Run \`skaile source \u2026\` from the shell.`
2438
+ );
2439
+ }
2440
+ function libraryShellHint(action) {
2441
+ state.message = pc5.yellow(
2442
+ `${action} is a shell command. Run \`skaile library \u2026\` from the shell.`
2407
2443
  );
2408
2444
  }
2409
2445
  async function showInfo() {
@@ -2417,9 +2453,9 @@ async function showInfo() {
2417
2453
  console.log(` ${S.rule(50)}`);
2418
2454
  if (e.version) console.log(` version: ${e.version}`);
2419
2455
  if (e.description) console.log(` description: ${e.description}`);
2420
- if (e.repository) console.log(` repository: ${pc5.dim(e.repository)}`);
2421
- console.log(` source: ${pc5.dim(e.source)}`);
2422
- console.log(` deployed: ${row.deployed ? pc5.green("yes") : pc5.red("no")}`);
2456
+ if (e.repository) console.log(` source: ${pc5.dim(e.repository)}`);
2457
+ console.log(` manifest: ${pc5.dim(e.source)}`);
2458
+ console.log(` installed: ${row.installed ? pc5.green("yes") : pc5.red("no")}`);
2423
2459
  if (e.requires.length) {
2424
2460
  console.log(` requires:`);
2425
2461
  for (const d of e.requires) console.log(` ${kindColor(d.kind)}:${d.name}`);
@@ -2441,11 +2477,11 @@ async function showInfo() {
2441
2477
  function expandUnderCursor() {
2442
2478
  const row = state.visibleRows[state.cursor];
2443
2479
  if (!row) return;
2444
- if (row.type === "repo-header" && state.collapsedRepos.has(row.repo)) {
2445
- state.collapsedRepos.delete(row.repo);
2480
+ if (row.type === "source-header" && state.collapsedSources.has(row.source)) {
2481
+ state.collapsedSources.delete(row.source);
2446
2482
  rebuildVisible();
2447
2483
  } else if (row.type === "header") {
2448
- const dk = `${row.repo}:${row.domain}`;
2484
+ const dk = `${row.source}:${row.domain}`;
2449
2485
  if (state.collapsedDomains.has(dk)) {
2450
2486
  state.collapsedDomains.delete(dk);
2451
2487
  rebuildVisible();
@@ -2455,15 +2491,15 @@ function expandUnderCursor() {
2455
2491
  function collapseOrJumpUnderCursor() {
2456
2492
  const row = state.visibleRows[state.cursor];
2457
2493
  if (!row) return;
2458
- if (row.type === "repo-header") {
2459
- if (!state.collapsedRepos.has(row.repo)) {
2460
- state.collapsedRepos.add(row.repo);
2494
+ if (row.type === "source-header") {
2495
+ if (!state.collapsedSources.has(row.source)) {
2496
+ state.collapsedSources.add(row.source);
2461
2497
  rebuildVisible();
2462
2498
  }
2463
2499
  return;
2464
2500
  }
2465
2501
  if (row.type === "header") {
2466
- const dk = `${row.repo}:${row.domain}`;
2502
+ const dk = `${row.source}:${row.domain}`;
2467
2503
  if (!state.collapsedDomains.has(dk)) {
2468
2504
  state.collapsedDomains.add(dk);
2469
2505
  rebuildVisible();
@@ -2471,7 +2507,7 @@ function collapseOrJumpUnderCursor() {
2471
2507
  const idx2 = findLastIndex(
2472
2508
  state.visibleRows,
2473
2509
  state.cursor,
2474
- (r) => r.type === "repo-header"
2510
+ (r) => r.type === "source-header"
2475
2511
  );
2476
2512
  if (idx2 >= 0) state.cursor = idx2;
2477
2513
  }
@@ -2480,7 +2516,7 @@ function collapseOrJumpUnderCursor() {
2480
2516
  const idx = findLastIndex(
2481
2517
  state.visibleRows,
2482
2518
  state.cursor,
2483
- (r) => r.type === "header" || r.type === "repo-header"
2519
+ (r) => r.type === "header" || r.type === "source-header"
2484
2520
  );
2485
2521
  if (idx >= 0) state.cursor = idx;
2486
2522
  }
@@ -2491,6 +2527,7 @@ function findLastIndex(arr, endExclusive, pred) {
2491
2527
  return -1;
2492
2528
  }
2493
2529
  var TAB_ASSETS = (t) => t === "assets";
2530
+ var TAB_SOURCES = (t) => t === "sources";
2494
2531
  var TAB_LIBRARIES = (t) => t === "libraries";
2495
2532
  var KEYMAP = [
2496
2533
  // Quit (with pending check) — handled here, not via a modal subroutine, so
@@ -2513,7 +2550,7 @@ var KEYMAP = [
2513
2550
  // Collapse / expand — assets tab only
2514
2551
  { keys: ["\x1B[C"], when: TAB_ASSETS, run: expandUnderCursor },
2515
2552
  { keys: ["\x1B[D"], when: TAB_ASSETS, run: collapseOrJumpUnderCursor },
2516
- // + : assets → bulk-add or single-add toggle; repos → add repo
2553
+ // + : assets → bulk-add or single-add toggle
2517
2554
  {
2518
2555
  keys: ["+"],
2519
2556
  when: TAB_ASSETS,
@@ -2526,9 +2563,28 @@ var KEYMAP = [
2526
2563
  }
2527
2564
  }
2528
2565
  },
2529
- // Libraries tab: +/-/r/d/n/enter all map to shell hints in the minimal scaffold.
2530
- { keys: ["+", "n"], when: TAB_LIBRARIES, run: () => shellHint("Adding a library") },
2531
- // - : assets bulk-remove or single-remove toggle; repos remove repo
2566
+ // Sources tab: +/-/n/r/x map to shell hints in the minimal scaffold.
2567
+ { keys: ["+", "n", "a"], when: TAB_SOURCES, run: () => sourceShellHint("Adding a source") },
2568
+ { keys: ["-", "r", "x"], when: TAB_SOURCES, run: () => sourceShellHint("Removing a source") },
2569
+ { keys: ["\r", "\n"], when: TAB_SOURCES, run: () => sourceShellHint("Source actions") },
2570
+ // Libraries tab: +/-/n/r/x/d/enter map to shell hints in the minimal scaffold.
2571
+ {
2572
+ keys: ["+", "n", "a"],
2573
+ when: TAB_LIBRARIES,
2574
+ run: () => libraryShellHint("Adding a library")
2575
+ },
2576
+ {
2577
+ keys: ["-", "r", "x"],
2578
+ when: TAB_LIBRARIES,
2579
+ run: () => libraryShellHint("Removing a library")
2580
+ },
2581
+ {
2582
+ keys: ["d"],
2583
+ when: TAB_LIBRARIES,
2584
+ run: () => libraryShellHint("Setting the default library")
2585
+ },
2586
+ { keys: ["\r", "\n"], when: TAB_LIBRARIES, run: () => libraryShellHint("Library actions") },
2587
+ // - : assets → bulk-remove or single-remove toggle
2532
2588
  {
2533
2589
  keys: ["-"],
2534
2590
  when: TAB_ASSETS,
@@ -2541,8 +2597,6 @@ var KEYMAP = [
2541
2597
  }
2542
2598
  }
2543
2599
  },
2544
- { keys: ["-", "r", "x"], when: TAB_LIBRARIES, run: () => shellHint("Removing a library") },
2545
- { keys: ["d"], when: TAB_LIBRARIES, run: () => shellHint("Setting the default library") },
2546
2600
  // Space — auto-toggle (single asset or bulk header)
2547
2601
  {
2548
2602
  keys: [" "],
@@ -2556,14 +2610,12 @@ var KEYMAP = [
2556
2610
  }
2557
2611
  }
2558
2612
  },
2559
- // s — sync (any tab) — minimal scaffold delegates to shell.
2560
- { keys: ["s"], run: syncLibraries },
2561
- // a : assets → apply; libraries → shell hint
2613
+ // s — sync all (any tab) — minimal scaffold delegates to shell.
2614
+ { keys: ["s"], run: syncAll },
2615
+ // a : assets → apply
2562
2616
  { keys: ["a"], when: TAB_ASSETS, run: applyChanges },
2563
- { keys: ["a"], when: TAB_LIBRARIES, run: () => shellHint("Adding a library") },
2564
- // Enter — apply on assets tab; libraries → shell hint
2617
+ // Enter apply on assets tab
2565
2618
  { keys: ["\r", "\n"], when: TAB_ASSETS, run: applyChanges },
2566
- { keys: ["\r", "\n"], when: TAB_LIBRARIES, run: () => shellHint("Library actions") },
2567
2619
  // Info — assets only
2568
2620
  { keys: ["i"], when: TAB_ASSETS, run: showInfo }
2569
2621
  ];
@@ -2591,8 +2643,8 @@ async function handleExitConfirm(key) {
2591
2643
  }
2592
2644
  async function run(projectDir) {
2593
2645
  am = new AssetManager({ projectDir });
2594
- loadAssets();
2595
- await loadLibraries();
2646
+ await loadAssets();
2647
+ await loadSourcesAndLibraries();
2596
2648
  hideCursor();
2597
2649
  process.stdin.setRawMode(true);
2598
2650
  process.stdin.resume();
@@ -2610,7 +2662,7 @@ async function run(projectDir) {
2610
2662
  clearScreen();
2611
2663
  }
2612
2664
  function makeManageCommand() {
2613
- return new Command("manage").description("Interactive TUI for managing libraries and assets").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
2665
+ return new Command("manage").description("Interactive TUI for managing assets, sources, and libraries").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
2614
2666
  await run(path15__default.resolve(opts.projectDir));
2615
2667
  });
2616
2668
  }
@@ -2689,9 +2741,9 @@ function makeNpxCommand() {
2689
2741
  });
2690
2742
  }
2691
2743
  spinner5.start("Syncing source");
2692
- const { LocalCatalogSource } = await import('../library/index.js');
2744
+ const { LocalCatalogSource: LocalCatalogSource2 } = await import('../library/index.js');
2693
2745
  const registry = createFullRegistry();
2694
- const catalogSource = new LocalCatalogSource(library, source.id, sourcePath, registry);
2746
+ const catalogSource = new LocalCatalogSource2(library, source.id, sourcePath, registry);
2695
2747
  const result = await catalogSource.sync();
2696
2748
  await library.syncSource(source.id);
2697
2749
  spinner5.stop(`Indexed ${result.assetsUpdated} assets`);
@@ -3588,7 +3640,7 @@ function makeInstallCommand() {
3588
3640
  const spinner6 = p5.spinner();
3589
3641
  spinner6.start(`Installing ${ref}`);
3590
3642
  try {
3591
- const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-XD7QYLMW.js');
3643
+ const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-T6RXQJTQ.js');
3592
3644
  const catalog = await openCatalogSource2({ projectDir: path15__default.resolve(opts.projectDir) });
3593
3645
  if (!supportsInstallManifest(catalog)) {
3594
3646
  throw new Error(
@@ -3618,8 +3670,23 @@ function makeInstallCommand() {
3618
3670
  }
3619
3671
  return;
3620
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
+ }
3621
3688
  const am2 = new AssetManager({
3622
- projectDir: path15__default.resolve(opts.projectDir),
3689
+ projectDir,
3623
3690
  driverTarget: opts.target
3624
3691
  });
3625
3692
  const spinner5 = p5.spinner();
@@ -3988,11 +4055,11 @@ function makeClearCommand() {
3988
4055
  }
3989
4056
  function makeReplCommand() {
3990
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) => {
3991
- const { findWorkspaceRoot } = await import('../core/index.js');
4058
+ const { findWorkspaceRoot: findWorkspaceRoot2 } = await import('../core/index.js');
3992
4059
  const { startRepl } = await import('../tui/index.js');
3993
4060
  const os = await import('os');
3994
4061
  const userCwd = path15__default.resolve(opts.projectDir ?? process.cwd());
3995
- const workspaceRoot = findWorkspaceRoot(userCwd);
4062
+ const workspaceRoot = findWorkspaceRoot2(userCwd);
3996
4063
  let projectDir;
3997
4064
  let agentCwd;
3998
4065
  let driverOverride = opts.driver;
@@ -4068,7 +4135,7 @@ async function runCompileTest(opts) {
4068
4135
  const { tmpdir } = await import('os');
4069
4136
  const { dirname: dirname3, join: join5, resolve: resolve4 } = await import('path');
4070
4137
  const { fileURLToPath: fileURLToPath2 } = await import('url');
4071
- const { unlinkSync, existsSync: existsSync11 } = await import('fs');
4138
+ const { unlinkSync, existsSync: existsSync12 } = await import('fs');
4072
4139
  const { spawn } = await import('child_process');
4073
4140
  const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
4074
4141
  const { MONOREPO_ROOT } = await import('../paths-FKKGS6BA.js');
@@ -4130,7 +4197,7 @@ async function runCompileTest(opts) {
4130
4197
  } catch {
4131
4198
  }
4132
4199
  try {
4133
- if (existsSync11(outfile)) unlinkSync(outfile);
4200
+ if (existsSync12(outfile)) unlinkSync(outfile);
4134
4201
  } catch {
4135
4202
  }
4136
4203
  }
@@ -4900,102 +4967,193 @@ function addSourceManifestCommands(src) {
4900
4967
  function sourcesDir() {
4901
4968
  return path15.join(skaileHomeDir(), "sources");
4902
4969
  }
4970
+ function sourceClonePath(name) {
4971
+ return path15.join(sourcesDir(), name);
4972
+ }
4903
4973
  function deriveSlug(url) {
4904
4974
  return url.replace(/\.git$/, "").split(/[/:]/).pop() ?? "source";
4905
4975
  }
4906
- function isSourceRow(lib) {
4907
- 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
+ }
4908
5044
  }
4909
5045
  function makeSourceCommand() {
4910
5046
  const cmd = new Command("source").description(
4911
- "Manage github sources (third-party repos of AI assets)"
5047
+ "Manage github sources for the current project (recorded in skaile.yaml)"
4912
5048
  );
4913
- 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();
4914
5051
  const slug = opts.name ?? deriveSlug(url);
4915
- const dest = path15.join(sourcesDir(), slug);
4916
- const clone = spawnSync("git", ["clone", url, dest], { stdio: "inherit" });
4917
- if (clone.status !== 0) {
4918
- 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
+ );
4919
5060
  process.exit(1);
4920
5061
  }
4921
- const { manager, close } = await openLibraryManager();
4922
- try {
4923
- await manager.addLibrary({
4924
- name: slug,
4925
- path: dest,
4926
- backend: "git",
4927
- backendConfig: { url, branch: "main", authHint: "ssh" },
4928
- ownership: "reader"
4929
- });
4930
- logOk(`Source "${slug}" added at ${dest}.`);
4931
- } finally {
4932
- 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}`);
4933
5073
  }
4934
- });
4935
- cmd.command("list").description("List registered sources").option("--json", "Output as JSON").action(async (opts) => {
4936
- const { manager, close } = await openLibraryManager();
4937
- try {
4938
- const all = await manager.listLibraries();
4939
- const sources = all.filter(isSourceRow);
4940
- if (opts.json) return console.log(JSON.stringify(sources, null, 2));
4941
- if (sources.length === 0) {
4942
- logInfo("No sources registered. Run `skaile source add <git-url>` to start.");
4943
- return;
4944
- }
4945
- console.log();
4946
- console.log(S.heading(" Sources"));
4947
- for (const s of sources) {
4948
- console.log(` ${S.cmd(s.name.padEnd(20))} ${S.dim(s.path)}`);
4949
- }
4950
- 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(`
4951
5096
  ${S.dim(`${sources.length} source(s)`)}
4952
5097
  `);
4953
- } finally {
4954
- close();
4955
- }
4956
5098
  });
4957
5099
  cmd.command("show <name>").description("Show details for a source").action(async (name) => {
4958
- const { manager, close } = await openLibraryManager();
4959
- try {
4960
- const s = await manager.requireLibrary(name);
4961
- if (!isSourceRow(s)) {
4962
- logErr(`"${name}" is a library, not a source \u2014 use \`skaile library show ${name}\`.`);
4963
- process.exit(1);
4964
- }
4965
- console.log(JSON.stringify(s, null, 2));
4966
- } finally {
4967
- 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);
4968
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
+ );
4969
5117
  });
4970
- cmd.command("remove <name>").description("Unregister a source").option("--purge", "Also delete the clone directory from disk", false).action(async (name, opts) => {
4971
- const { manager, close } = await openLibraryManager();
4972
- try {
4973
- const s = await manager.requireLibrary(name);
4974
- if (!isSourceRow(s)) {
4975
- logErr(`"${name}" is a library, not a source \u2014 use \`skaile library remove ${name}\`.`);
4976
- process.exit(1);
4977
- }
4978
- await manager.removeLibrary(name, { purge: opts.purge });
4979
- logOk(`Removed source "${name}".`);
4980
- } finally {
4981
- 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);
4982
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
+ );
4983
5131
  });
4984
- cmd.command("sync [name]").description("Re-fetch upstream changes for one or all sources").action(async (name) => {
4985
- const { manager, close } = await openLibraryManager();
4986
- try {
4987
- const all = await manager.listLibraries();
4988
- const targets = name ? all.filter((l) => l.name === name && isSourceRow(l)) : all.filter(isSourceRow);
4989
- if (targets.length === 0) {
4990
- logInfo(name ? `No source named "${name}".` : "No sources registered.");
4991
- return;
4992
- }
4993
- for (const s of targets) {
4994
- const res = await manager.driverFor(s.backend).pull(s);
4995
- 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
+ }
4996
5151
  }
4997
- } finally {
4998
- 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}`);
4999
5157
  }
5000
5158
  });
5001
5159
  cmd.command("patch <ref>").description("Extract an asset for editing against the source").action((_ref) => {
@@ -5761,7 +5919,7 @@ program.command("init [project-dir]").description("Initialize a project director
5761
5919
  ).option("--no-git", "Skip git init and .gitignore").action(
5762
5920
  async (projectDir, opts) => {
5763
5921
  const { execSync: execSync3 } = await import('child_process');
5764
- 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');
5765
5923
  const { stringify } = await import('yaml');
5766
5924
  const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
5767
5925
  const backend = opts.backend;
@@ -5775,22 +5933,22 @@ program.command("init [project-dir]").description("Initialize a project director
5775
5933
  const resolved = path15__default.resolve(projectDir ?? ".");
5776
5934
  const projectName = path15__default.basename(resolved);
5777
5935
  const created = [];
5778
- if (!existsSync11(resolved)) {
5936
+ if (!existsSync12(resolved)) {
5779
5937
  mkdirSync3(resolved, { recursive: true });
5780
5938
  created.push(".");
5781
5939
  }
5782
5940
  const skaileDir = path15__default.join(resolved, ".skaile");
5783
- if (!existsSync11(skaileDir)) {
5941
+ if (!existsSync12(skaileDir)) {
5784
5942
  mkdirSync3(path15__default.join(skaileDir, "sessions"), { recursive: true });
5785
5943
  created.push(".skaile/");
5786
5944
  }
5787
5945
  const settingsPath = path15__default.join(skaileDir, "settings.json");
5788
- if (!existsSync11(settingsPath)) {
5946
+ if (!existsSync12(settingsPath)) {
5789
5947
  writeFileSync2(settingsPath, "{}\n");
5790
5948
  created.push(".skaile/settings.json");
5791
5949
  }
5792
5950
  const wsConfigPath = path15__default.join(resolved, "skaile.yaml");
5793
- if (!existsSync11(wsConfigPath)) {
5951
+ if (!existsSync12(wsConfigPath)) {
5794
5952
  writeFileSync2(
5795
5953
  wsConfigPath,
5796
5954
  stringify({
@@ -5812,7 +5970,7 @@ program.command("init [project-dir]").description("Initialize a project director
5812
5970
  }
5813
5971
  for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
5814
5972
  const full = path15__default.join(resolved, dir);
5815
- if (!existsSync11(full)) {
5973
+ if (!existsSync12(full)) {
5816
5974
  mkdirSync3(full, { recursive: true });
5817
5975
  created.push(`${dir}/`);
5818
5976
  }
@@ -5820,7 +5978,7 @@ program.command("init [project-dir]").description("Initialize a project director
5820
5978
  if (opts.git) {
5821
5979
  const gitignorePath = path15__default.join(resolved, ".gitignore");
5822
5980
  const entries = ["node_modules/", ".skaile/sessions/", "*.log", ".env", ".env.local"];
5823
- const existing = existsSync11(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : "";
5981
+ const existing = existsSync12(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : "";
5824
5982
  const have = new Set(existing.split("\n").map((l) => l.trim()));
5825
5983
  const append = entries.filter((e) => !have.has(e));
5826
5984
  if (append.length > 0) {
@@ -5829,7 +5987,7 @@ program.command("init [project-dir]").description("Initialize a project director
5829
5987
  `);
5830
5988
  if (!existing) created.push(".gitignore");
5831
5989
  }
5832
- if (!existsSync11(path15__default.join(resolved, ".git"))) {
5990
+ if (!existsSync12(path15__default.join(resolved, ".git"))) {
5833
5991
  try {
5834
5992
  execSync3("git init", { cwd: resolved, stdio: "pipe" });
5835
5993
  created.push(".git/");
@@ -5855,6 +6013,17 @@ program.command("init [project-dir]").description("Initialize a project director
5855
6013
  ` ${S.dim(`\u26A0 could not bootstrap user config: ${err instanceof Error ? err.message : String(err)}`)}`
5856
6014
  );
5857
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
+ }
5858
6027
  for (const c of created) console.log(` ${S.dim(`+ ${c}`)}`);
5859
6028
  logOk(`Initialized project at ${S.heading(resolved)}`);
5860
6029
  console.log();
@@ -5872,7 +6041,7 @@ program.command("init [project-dir]").description("Initialize a project director
5872
6041
  }
5873
6042
  );
5874
6043
  program.command("setup").description("Interactive provider setup wizard").action(async () => {
5875
- const { cmdSetup } = await import('../setup-WZFCLQ2J.js');
6044
+ const { cmdSetup } = await import('../setup-R6VWIPLL.js');
5876
6045
  await cmdSetup([], { projectDir: process.cwd() });
5877
6046
  });
5878
6047
  program.addCommand(makeInstallCommand());