@portel/photon 1.17.6 → 1.19.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 (194) hide show
  1. package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
  2. package/dist/auto-ui/beam/photon-management.js +28 -1
  3. package/dist/auto-ui/beam/photon-management.js.map +1 -1
  4. package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -1
  5. package/dist/auto-ui/beam/routes/api-marketplace.js +10 -5
  6. package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
  7. package/dist/auto-ui/beam.d.ts.map +1 -1
  8. package/dist/auto-ui/beam.js +14 -4
  9. package/dist/auto-ui/beam.js.map +1 -1
  10. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
  11. package/dist/auto-ui/streamable-http-transport.js +259 -88
  12. package/dist/auto-ui/streamable-http-transport.js.map +1 -1
  13. package/dist/auto-ui/types.d.ts +2 -0
  14. package/dist/auto-ui/types.d.ts.map +1 -1
  15. package/dist/auto-ui/types.js +5 -0
  16. package/dist/auto-ui/types.js.map +1 -1
  17. package/dist/beam-form.bundle.js +5 -3
  18. package/dist/beam-form.bundle.js.map +2 -2
  19. package/dist/beam.bundle.js +912 -59
  20. package/dist/beam.bundle.js.map +3 -3
  21. package/dist/claude-code-plugin.js +1 -1
  22. package/dist/cli/commands/beam.d.ts.map +1 -1
  23. package/dist/cli/commands/beam.js +8 -2
  24. package/dist/cli/commands/beam.js.map +1 -1
  25. package/dist/cli/commands/changelog.d.ts +9 -0
  26. package/dist/cli/commands/changelog.d.ts.map +1 -0
  27. package/dist/cli/commands/changelog.js +133 -0
  28. package/dist/cli/commands/changelog.js.map +1 -0
  29. package/dist/cli/commands/maker.d.ts.map +1 -1
  30. package/dist/cli/commands/maker.js +23 -2
  31. package/dist/cli/commands/maker.js.map +1 -1
  32. package/dist/cli/commands/mcp.d.ts.map +1 -1
  33. package/dist/cli/commands/mcp.js +53 -0
  34. package/dist/cli/commands/mcp.js.map +1 -1
  35. package/dist/cli/commands/package.d.ts.map +1 -1
  36. package/dist/cli/commands/package.js +18 -2
  37. package/dist/cli/commands/package.js.map +1 -1
  38. package/dist/cli/commands/run.d.ts.map +1 -1
  39. package/dist/cli/commands/run.js +1 -0
  40. package/dist/cli/commands/run.js.map +1 -1
  41. package/dist/cli/commands/update.d.ts +3 -2
  42. package/dist/cli/commands/update.d.ts.map +1 -1
  43. package/dist/cli/commands/update.js +50 -43
  44. package/dist/cli/commands/update.js.map +1 -1
  45. package/dist/cli/index.d.ts.map +1 -1
  46. package/dist/cli/index.js +16 -2
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/cli-alias.js +1 -1
  49. package/dist/cli-alias.js.map +1 -1
  50. package/dist/context-store.d.ts +23 -33
  51. package/dist/context-store.d.ts.map +1 -1
  52. package/dist/context-store.js +147 -97
  53. package/dist/context-store.js.map +1 -1
  54. package/dist/context.d.ts +15 -10
  55. package/dist/context.d.ts.map +1 -1
  56. package/dist/context.js +37 -13
  57. package/dist/context.js.map +1 -1
  58. package/dist/daemon/server.js +4 -2
  59. package/dist/daemon/server.js.map +1 -1
  60. package/dist/data-migration.d.ts +27 -0
  61. package/dist/data-migration.d.ts.map +1 -0
  62. package/dist/data-migration.js +307 -0
  63. package/dist/data-migration.js.map +1 -0
  64. package/dist/editor-support/docblock-tag-catalog.d.ts.map +1 -1
  65. package/dist/editor-support/docblock-tag-catalog.js +6 -0
  66. package/dist/editor-support/docblock-tag-catalog.js.map +1 -1
  67. package/dist/loader.d.ts +10 -0
  68. package/dist/loader.d.ts.map +1 -1
  69. package/dist/loader.js +106 -20
  70. package/dist/loader.js.map +1 -1
  71. package/dist/marketplace-manager.d.ts.map +1 -1
  72. package/dist/marketplace-manager.js +25 -5
  73. package/dist/marketplace-manager.js.map +1 -1
  74. package/dist/photon-cli-runner.d.ts.map +1 -1
  75. package/dist/photon-cli-runner.js +56 -19
  76. package/dist/photon-cli-runner.js.map +1 -1
  77. package/dist/photon-doc-extractor.d.ts +1 -0
  78. package/dist/photon-doc-extractor.d.ts.map +1 -1
  79. package/dist/photon-doc-extractor.js +6 -0
  80. package/dist/photon-doc-extractor.js.map +1 -1
  81. package/dist/readme-syncer.d.ts.map +1 -1
  82. package/dist/readme-syncer.js +6 -1
  83. package/dist/readme-syncer.js.map +1 -1
  84. package/dist/server.d.ts +40 -0
  85. package/dist/server.d.ts.map +1 -1
  86. package/dist/server.js +298 -29
  87. package/dist/server.js.map +1 -1
  88. package/dist/shared/audit.js +4 -4
  89. package/dist/shared/audit.js.map +1 -1
  90. package/dist/tasks/executor.d.ts +47 -0
  91. package/dist/tasks/executor.d.ts.map +1 -0
  92. package/dist/tasks/executor.js +180 -0
  93. package/dist/tasks/executor.js.map +1 -0
  94. package/dist/tasks/store.d.ts +13 -6
  95. package/dist/tasks/store.d.ts.map +1 -1
  96. package/dist/tasks/store.js +56 -11
  97. package/dist/tasks/store.js.map +1 -1
  98. package/dist/tasks/types.d.ts +23 -2
  99. package/dist/tasks/types.d.ts.map +1 -1
  100. package/dist/tasks/types.js +23 -3
  101. package/dist/tasks/types.js.map +1 -1
  102. package/dist/version-notify.d.ts +27 -0
  103. package/dist/version-notify.d.ts.map +1 -0
  104. package/dist/version-notify.js +142 -0
  105. package/dist/version-notify.js.map +1 -0
  106. package/package.json +5 -4
  107. package/dist/auto-ui/bridge/openai-shim.d.ts +0 -20
  108. package/dist/auto-ui/bridge/openai-shim.d.ts.map +0 -1
  109. package/dist/auto-ui/bridge/openai-shim.js +0 -231
  110. package/dist/auto-ui/bridge/openai-shim.js.map +0 -1
  111. package/dist/auto-ui/bridge/photon-app.d.ts +0 -162
  112. package/dist/auto-ui/bridge/photon-app.d.ts.map +0 -1
  113. package/dist/auto-ui/bridge/photon-app.js +0 -460
  114. package/dist/auto-ui/bridge/photon-app.js.map +0 -1
  115. package/dist/auto-ui/daemon-tools.d.ts +0 -45
  116. package/dist/auto-ui/daemon-tools.d.ts.map +0 -1
  117. package/dist/auto-ui/daemon-tools.js +0 -581
  118. package/dist/auto-ui/daemon-tools.js.map +0 -1
  119. package/dist/auto-ui/design-system/index.d.ts +0 -21
  120. package/dist/auto-ui/design-system/index.d.ts.map +0 -1
  121. package/dist/auto-ui/design-system/index.js +0 -27
  122. package/dist/auto-ui/design-system/index.js.map +0 -1
  123. package/dist/auto-ui/design-system/transaction-ui.d.ts +0 -70
  124. package/dist/auto-ui/design-system/transaction-ui.d.ts.map +0 -1
  125. package/dist/auto-ui/design-system/transaction-ui.js +0 -982
  126. package/dist/auto-ui/design-system/transaction-ui.js.map +0 -1
  127. package/dist/auto-ui/playground-server.d.ts +0 -7
  128. package/dist/auto-ui/playground-server.d.ts.map +0 -1
  129. package/dist/auto-ui/playground-server.js +0 -840
  130. package/dist/auto-ui/playground-server.js.map +0 -1
  131. package/dist/auto-ui/rendering/components.d.ts +0 -29
  132. package/dist/auto-ui/rendering/components.d.ts.map +0 -1
  133. package/dist/auto-ui/rendering/components.js +0 -1341
  134. package/dist/auto-ui/rendering/components.js.map +0 -1
  135. package/dist/auto-ui/rendering/field-analyzer.d.ts +0 -104
  136. package/dist/auto-ui/rendering/field-analyzer.d.ts.map +0 -1
  137. package/dist/auto-ui/rendering/field-analyzer.js +0 -447
  138. package/dist/auto-ui/rendering/field-analyzer.js.map +0 -1
  139. package/dist/auto-ui/rendering/field-renderers.d.ts +0 -64
  140. package/dist/auto-ui/rendering/field-renderers.d.ts.map +0 -1
  141. package/dist/auto-ui/rendering/field-renderers.js +0 -317
  142. package/dist/auto-ui/rendering/field-renderers.js.map +0 -1
  143. package/dist/auto-ui/rendering/index.d.ts +0 -28
  144. package/dist/auto-ui/rendering/index.d.ts.map +0 -1
  145. package/dist/auto-ui/rendering/index.js +0 -60
  146. package/dist/auto-ui/rendering/index.js.map +0 -1
  147. package/dist/auto-ui/rendering/layout-selector.d.ts +0 -60
  148. package/dist/auto-ui/rendering/layout-selector.d.ts.map +0 -1
  149. package/dist/auto-ui/rendering/layout-selector.js +0 -476
  150. package/dist/auto-ui/rendering/layout-selector.js.map +0 -1
  151. package/dist/markdown-utils.d.ts +0 -8
  152. package/dist/markdown-utils.d.ts.map +0 -1
  153. package/dist/markdown-utils.js +0 -64
  154. package/dist/markdown-utils.js.map +0 -1
  155. package/dist/mcp-client.d.ts +0 -9
  156. package/dist/mcp-client.d.ts.map +0 -1
  157. package/dist/mcp-client.js +0 -11
  158. package/dist/mcp-client.js.map +0 -1
  159. package/dist/mcp-elicitation.d.ts +0 -32
  160. package/dist/mcp-elicitation.d.ts.map +0 -1
  161. package/dist/mcp-elicitation.js +0 -26
  162. package/dist/mcp-elicitation.js.map +0 -1
  163. package/dist/photons/builder-compass.photon.d.ts +0 -167
  164. package/dist/photons/builder-compass.photon.d.ts.map +0 -1
  165. package/dist/photons/builder-compass.photon.js +0 -816
  166. package/dist/photons/builder-compass.photon.js.map +0 -1
  167. package/dist/photons/builder-compass.photon.ts +0 -1129
  168. package/dist/photons/docs/ui/docs.html +0 -441
  169. package/dist/photons/docs.photon.d.ts +0 -237
  170. package/dist/photons/docs.photon.d.ts.map +0 -1
  171. package/dist/photons/docs.photon.js +0 -483
  172. package/dist/photons/docs.photon.js.map +0 -1
  173. package/dist/photons/docs.photon.ts +0 -536
  174. package/dist/photons/slides.photon.d.ts +0 -212
  175. package/dist/photons/slides.photon.d.ts.map +0 -1
  176. package/dist/photons/slides.photon.js +0 -355
  177. package/dist/photons/slides.photon.js.map +0 -1
  178. package/dist/photons/slides.photon.ts +0 -370
  179. package/dist/photons/spreadsheet/ui/spreadsheet.html +0 -779
  180. package/dist/photons/spreadsheet.photon.d.ts +0 -554
  181. package/dist/photons/spreadsheet.photon.d.ts.map +0 -1
  182. package/dist/photons/spreadsheet.photon.js +0 -1050
  183. package/dist/photons/spreadsheet.photon.js.map +0 -1
  184. package/dist/photons/spreadsheet.photon.ts +0 -1239
  185. package/dist/photons/ui/builder-compass.html +0 -1199
  186. package/dist/photons/ui/builder-compass.photon.html +0 -380
  187. package/dist/security-scanner.d.ts +0 -52
  188. package/dist/security-scanner.d.ts.map +0 -1
  189. package/dist/security-scanner.js +0 -181
  190. package/dist/security-scanner.js.map +0 -1
  191. package/dist/shared/performance.d.ts +0 -65
  192. package/dist/shared/performance.d.ts.map +0 -1
  193. package/dist/shared/performance.js +0 -136
  194. package/dist/shared/performance.js.map +0 -1
package/dist/loader.js CHANGED
@@ -36,7 +36,7 @@ executionContext,
36
36
  // Lock helper
37
37
  withLock as withLockHelper,
38
38
  // Middleware system
39
- builtinRegistry, MiddlewareRegistry, buildMiddlewareChain, } from '@portel/photon-core';
39
+ builtinRegistry, MiddlewareRegistry, buildMiddlewareChain, detectNamespace, getCacheDir, } from '@portel/photon-core';
40
40
  import { getDefaultContext } from './context.js';
41
41
  import * as os from 'os';
42
42
  import { MarketplaceManager } from './marketplace-manager.js';
@@ -332,7 +332,7 @@ export class PhotonLoader {
332
332
  * Directory where MCP-specific dependencies are cached
333
333
  */
334
334
  getDependencyCacheDir(cacheKey) {
335
- return path.join(os.homedir(), '.cache', 'photon-mcp', 'dependencies', cacheKey);
335
+ return path.join(getCacheDir(), 'dependencies', cacheKey);
336
336
  }
337
337
  getBuildCacheDir(cacheKey) {
338
338
  return path.join(this.getDependencyCacheDir(cacheKey), '.build');
@@ -402,22 +402,27 @@ export class PhotonLoader {
402
402
  ? this.dependenciesEqual(metadata.dependencies, dependencies)
403
403
  : false;
404
404
  const resolvedCoreVersion = getResolvedPhotonCoreVersion();
405
- const coreVersionChanged = metadata?.photonCoreVersion !== resolvedCoreVersion;
406
- const needsClear = Boolean(metadata && (!hashMatches || !depsMatch || coreVersionChanged));
407
- if (needsClear) {
408
- const depDir = this.getDependencyCacheDir(cacheKey);
409
- const buildDir = this.getBuildCacheDir(cacheKey);
405
+ // Compare versions tolerantly: "2.17.6" matches "^2.17.6" and vice versa
406
+ // This prevents unnecessary cache clears when createRequire fallback returns a range
407
+ const storedVersion = metadata?.photonCoreVersion || '';
408
+ const coreVersionChanged = storedVersion !== resolvedCoreVersion &&
409
+ storedVersion.replace(/^[\^~]/, '') !== resolvedCoreVersion.replace(/^[\^~]/, '');
410
+ // Only clear dependency cache when deps or core version actually changed.
411
+ // Source-only changes just need a build cache clear (recompile is fast).
412
+ const needsDepClear = Boolean(metadata && (!depsMatch || coreVersionChanged));
413
+ const needsBuildClear = Boolean(metadata && !hashMatches);
414
+ if (needsDepClear) {
410
415
  if (coreVersionChanged) {
411
416
  this.log(`🔄 photon-core version changed (${metadata?.photonCoreVersion} → ${resolvedCoreVersion}), clearing cache for ${mcpName}`);
412
417
  }
413
418
  else {
414
- this.log(`🔄 Dependencies changed for ${mcpName} (${cacheKey}), clearing caches`, {
415
- dependencyCache: depDir,
416
- buildCache: buildDir,
417
- });
419
+ this.log(`🔄 Dependencies changed for ${mcpName}, reinstalling`);
418
420
  }
419
421
  await this.clearAllCaches(cacheKey);
420
422
  }
423
+ else if (needsBuildClear) {
424
+ await this.clearBuildCache(cacheKey);
425
+ }
421
426
  let nodeModules = null;
422
427
  if (dependencies.length > 0) {
423
428
  nodeModules = await this.dependencyManager.ensureDependencies(cacheKey, dependencies);
@@ -456,24 +461,39 @@ export class PhotonLoader {
456
461
  const message = error instanceof Error ? error.message : String(error);
457
462
  return (message.includes('Cannot find package') ||
458
463
  message.includes('ERR_MODULE_NOT_FOUND') ||
459
- message.includes('Cannot find module'));
464
+ message.includes('Cannot find module') ||
465
+ message.includes('require is not defined'));
460
466
  }
461
467
  static parseDependenciesFromSource(source) {
462
468
  const deps = [];
469
+ // Only match @dependencies inside JSDoc blocks (/** ... */)
470
+ const jsdocBlocks = source.match(/\/\*\*[\s\S]*?\*\//g) || [];
471
+ const jsdocText = jsdocBlocks.join('\n');
463
472
  const regex = /@dependencies\s+([^\r\n]+)/g;
464
473
  let match;
465
- while ((match = regex.exec(source)) !== null) {
474
+ while ((match = regex.exec(jsdocText)) !== null) {
466
475
  const entries = match[1]
476
+ .replace(/\*\/$/, '') // strip trailing */ if on same line
467
477
  .split(',')
468
478
  .map((entry) => entry.trim())
469
479
  .filter(Boolean);
470
480
  for (const entry of entries) {
471
481
  const atIndex = entry.lastIndexOf('@');
482
+ let name;
483
+ let version;
472
484
  if (atIndex <= 0) {
485
+ // No version specified (e.g., "sharp") — default to latest
486
+ name = entry.trim();
487
+ version = '*';
488
+ }
489
+ else {
490
+ name = entry.slice(0, atIndex).trim();
491
+ version = entry.slice(atIndex + 1).trim();
492
+ }
493
+ // Validate: npm package names are lowercase, may have @scope/, no spaces
494
+ if (name.includes(' ') || !/^(@[a-z0-9-]+\/)?[a-z0-9._-]+$/.test(name)) {
473
495
  continue;
474
496
  }
475
- const name = entry.slice(0, atIndex).trim();
476
- let version = entry.slice(atIndex + 1).trim();
477
497
  // Trailing ? marks the dependency as optional (e.g. sharp@^0.33.0?)
478
498
  const optional = version.endsWith('?');
479
499
  if (optional) {
@@ -608,8 +628,9 @@ export class PhotonLoader {
608
628
  this.logger.warn(`⚠️ ${name} loaded with configuration warnings:`);
609
629
  this.logger.warn(String(configError));
610
630
  }
611
- // Set photon name for event source identification
631
+ // Set photon name and namespace for event source identification and data paths
612
632
  instance._photonName = name;
633
+ instance._photonNamespace = this.resolveNamespace(absolutePath);
613
634
  // Inject instance name for named instances (runtime concept, not code)
614
635
  instance.instanceName = options?.instanceName ?? '';
615
636
  // Inject file path for storage()/assets() resolution
@@ -701,11 +722,12 @@ export class PhotonLoader {
701
722
  };
702
723
  }
703
724
  if (caps.has('memory')) {
704
- // Inject lazy memory provider
725
+ // Inject lazy memory provider — capture baseDir from loader context
726
+ const memoryBaseDir = this.baseDir;
705
727
  Object.defineProperty(instance, 'memory', {
706
728
  get() {
707
729
  if (!this._memory) {
708
- this._memory = new MemoryProvider(name, this._sessionId);
730
+ this._memory = new MemoryProvider(name, this._sessionId, this._photonNamespace, memoryBaseDir);
709
731
  }
710
732
  return this._memory;
711
733
  },
@@ -903,6 +925,42 @@ export class PhotonLoader {
903
925
  }
904
926
  this.log(`🔌 Injected channel event infrastructure into ${name}`);
905
927
  }
928
+ // Inject this.channel() for channel notifications — works for both Photon
929
+ // subclasses and plain classes. Emits on '{photonName}:channel-push' daemon
930
+ // channel which the MCP server intercepts and translates to the client's
931
+ // notification method (e.g. notifications/claude/channel).
932
+ //
933
+ // this.channel(content, meta) — send a message to the client
934
+ // this.channel.respond(id, behavior) — respond to permission requests
935
+ // this.channel.onPermission(handler) — register permission request handler
936
+ if (typeof instance.emit === 'function') {
937
+ const emitFn = instance.emit.bind(instance);
938
+ // Permission handler stored locally — wired by server after load
939
+ let permissionHandler;
940
+ const channelFn = Object.assign((content, meta) => {
941
+ emitFn({
942
+ channel: `${name}:channel-push`,
943
+ event: 'channel',
944
+ data: { content, meta },
945
+ });
946
+ }, {
947
+ respond: (requestId, behavior) => {
948
+ emitFn({
949
+ channel: `${name}:channel-permission-response`,
950
+ event: 'permission-response',
951
+ data: { request_id: requestId, behavior },
952
+ });
953
+ },
954
+ onPermission: (handler) => {
955
+ permissionHandler = handler;
956
+ },
957
+ // Internal: called by the server when a permission request arrives
958
+ _dispatchPermission: (request) => {
959
+ permissionHandler?.(request);
960
+ },
961
+ });
962
+ instance.channel = channelFn;
963
+ }
906
964
  // Check @cli dependencies (required system CLI tools)
907
965
  if (tsContent) {
908
966
  await this.checkCLIDependencies(tsContent, name);
@@ -1036,6 +1094,7 @@ export class PhotonLoader {
1036
1094
  instance._photonConfigError = configError;
1037
1095
  }
1038
1096
  instance._photonName = name;
1097
+ instance._photonNamespace = this.resolveNamespace(absolutePath);
1039
1098
  instance.instanceName = options?.instanceName ?? '';
1040
1099
  // Inject file path for storage()/assets() resolution.
1041
1100
  // For preloaded modules (compiled binaries), remap to ~/.photon/ so storage()
@@ -1115,10 +1174,11 @@ export class PhotonLoader {
1115
1174
  };
1116
1175
  }
1117
1176
  if (caps.has('memory')) {
1177
+ const memoryBaseDir = this.baseDir;
1118
1178
  Object.defineProperty(instance, 'memory', {
1119
1179
  get() {
1120
1180
  if (!this._memory) {
1121
- this._memory = new MemoryProvider(name, this._sessionId);
1181
+ this._memory = new MemoryProvider(name, this._sessionId, this._photonNamespace, memoryBaseDir);
1122
1182
  }
1123
1183
  return this._memory;
1124
1184
  },
@@ -1944,7 +2004,7 @@ export class PhotonLoader {
1944
2004
  }
1945
2005
  // If configured but failed, provide more specific error
1946
2006
  throw new Error(`MCP "${dep.name}" is configured but failed to connect: ${errorMsg}\n` +
1947
- `Check your ~/.photon/config.json configuration and ensure:\n` +
2007
+ `Check your config.json configuration and ensure:\n` +
1948
2008
  ` • The command/URL is correct\n` +
1949
2009
  ` • Required environment variables are set\n` +
1950
2010
  ` • The MCP server is accessible`);
@@ -2989,12 +3049,19 @@ Run: photon mcp ${mcpName} --config
2989
3049
  return; // No emit function, can't wrap methods
2990
3050
  }
2991
3051
  // Get all public method names from the instance
3052
+ // Skip runtime-injected methods (emit, render, push, ask) — these are
3053
+ // capability methods injected by the loader, not user-defined tools
3054
+ const RUNTIME_METHODS = new Set(['emit', 'render', 'channel', 'ask', 'call']);
2992
3055
  const proto = Object.getPrototypeOf(instance);
2993
3056
  const methodNames = Object.getOwnPropertyNames(proto).filter((name) => {
2994
3057
  // Skip constructor and private/protected methods
2995
3058
  if (name === 'constructor' || name.startsWith('_')) {
2996
3059
  return false;
2997
3060
  }
3061
+ // Skip runtime-injected capability methods
3062
+ if (RUNTIME_METHODS.has(name)) {
3063
+ return false;
3064
+ }
2998
3065
  const descriptor = Object.getOwnPropertyDescriptor(proto, name);
2999
3066
  return descriptor && typeof descriptor.value === 'function';
3000
3067
  });
@@ -3210,6 +3277,25 @@ Run: photon mcp ${mcpName} --config
3210
3277
  check.on('error', () => resolve(false));
3211
3278
  });
3212
3279
  }
3280
+ /**
3281
+ * Resolve the namespace for a photon based on its file path.
3282
+ * Extracts the directory name between baseDir and the photon file.
3283
+ *
3284
+ * Examples:
3285
+ * ~/.photon/portel-dev/todo.photon.ts → 'portel-dev'
3286
+ * ~/.photon/local/todo.photon.ts → 'local'
3287
+ * ~/.photon/todo.photon.ts → detected from git or 'local'
3288
+ */
3289
+ resolveNamespace(absolutePath) {
3290
+ const rel = path.relative(this.baseDir, absolutePath);
3291
+ const parts = rel.split(path.sep);
3292
+ // If file is in a subdirectory (namespace/photon.ts), use that as namespace
3293
+ if (parts.length >= 2) {
3294
+ return parts[0];
3295
+ }
3296
+ // Flat file at root — detect from git remote or default to 'local'
3297
+ return detectNamespace(this.baseDir);
3298
+ }
3213
3299
  /**
3214
3300
  * Discover and extract assets from a Photon file
3215
3301
  * Uses shared discoverAssets from photon-core for core logic,