@portel/photon-core 1.0.0 → 1.0.2

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.
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Path Resolver for Photon files
3
+ *
4
+ * Generic path resolution utilities used by photon, lumina, ncp.
5
+ * Configurable file extensions and default directories.
6
+ */
7
+
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import * as os from 'os';
11
+
12
+ export const DEFAULT_PHOTON_DIR = path.join(os.homedir(), '.photon');
13
+
14
+ export interface ResolverOptions {
15
+ /** File extensions to look for (default: ['.photon.ts', '.photon.js']) */
16
+ extensions?: string[];
17
+ /** Default working directory */
18
+ defaultDir?: string;
19
+ }
20
+
21
+ const defaultOptions: Required<ResolverOptions> = {
22
+ extensions: ['.photon.ts', '.photon.js'],
23
+ defaultDir: DEFAULT_PHOTON_DIR,
24
+ };
25
+
26
+ /**
27
+ * Resolve a file path from name
28
+ * Looks in the specified working directory, or uses absolute path if provided
29
+ */
30
+ export async function resolvePath(
31
+ name: string,
32
+ workingDir?: string,
33
+ options?: ResolverOptions
34
+ ): Promise<string | null> {
35
+ const opts = { ...defaultOptions, ...options };
36
+ const dir = workingDir || opts.defaultDir;
37
+
38
+ // If absolute path provided, check if it exists
39
+ if (path.isAbsolute(name)) {
40
+ try {
41
+ await fs.access(name);
42
+ return name;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ // Remove extension if provided (match any configured extension)
49
+ let basename = name;
50
+ for (const ext of opts.extensions) {
51
+ if (name.endsWith(ext)) {
52
+ basename = name.slice(0, -ext.length);
53
+ break;
54
+ }
55
+ }
56
+
57
+ // Try each extension
58
+ for (const ext of opts.extensions) {
59
+ const filePath = path.join(dir, `${basename}${ext}`);
60
+ try {
61
+ await fs.access(filePath);
62
+ return filePath;
63
+ } catch {
64
+ // Continue to next extension
65
+ }
66
+ }
67
+
68
+ // Not found
69
+ return null;
70
+ }
71
+
72
+ /**
73
+ * List all matching files in a directory
74
+ */
75
+ export async function listFiles(
76
+ workingDir?: string,
77
+ options?: ResolverOptions
78
+ ): Promise<string[]> {
79
+ const opts = { ...defaultOptions, ...options };
80
+ const dir = workingDir || opts.defaultDir;
81
+
82
+ try {
83
+ // Ensure directory exists
84
+ await fs.mkdir(dir, { recursive: true });
85
+
86
+ const entries = await fs.readdir(dir, { withFileTypes: true });
87
+ const files: string[] = [];
88
+
89
+ for (const entry of entries) {
90
+ if (entry.isFile()) {
91
+ // Check if file matches any extension
92
+ for (const ext of opts.extensions) {
93
+ if (entry.name.endsWith(ext)) {
94
+ // Remove extension for display
95
+ const name = entry.name.slice(0, -ext.length);
96
+ files.push(name);
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ return files.sort();
104
+ } catch {
105
+ return [];
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Ensure directory exists
111
+ */
112
+ export async function ensureDir(dir?: string): Promise<void> {
113
+ const targetDir = dir || DEFAULT_PHOTON_DIR;
114
+ await fs.mkdir(targetDir, { recursive: true });
115
+ }
116
+
117
+ // Convenience aliases for photon-specific usage
118
+ export const resolvePhotonPath = resolvePath;
119
+ export const listPhotonFiles = listFiles;
120
+ export const ensurePhotonDir = ensureDir;
@@ -10,7 +10,7 @@
10
10
 
11
11
  import * as fs from 'fs/promises';
12
12
  import * as ts from 'typescript';
13
- import { ExtractedSchema, ConstructorParam, TemplateInfo, StaticInfo } from './types.js';
13
+ import { ExtractedSchema, ConstructorParam, TemplateInfo, StaticInfo, OutputFormat } from './types.js';
14
14
 
15
15
  export interface ExtractedMetadata {
16
16
  tools: ExtractedSchema[];
@@ -125,12 +125,12 @@ export class SchemaExtractor {
125
125
  }
126
126
  // Otherwise, it's a regular tool
127
127
  else {
128
- const format = this.extractFormat(jsdoc);
128
+ const outputFormat = this.extractFormat(jsdoc);
129
129
  tools.push({
130
130
  name: methodName,
131
131
  description,
132
132
  inputSchema,
133
- ...(format ? { format } : {}),
133
+ ...(outputFormat ? { outputFormat } : {}),
134
134
  });
135
135
  }
136
136
  };
@@ -813,12 +813,28 @@ export class SchemaExtractor {
813
813
  /**
814
814
  * Extract format hint from @format tag
815
815
  * Example: @format table
816
+ * Example: @format json
817
+ * Example: @format code:typescript
816
818
  */
817
- private extractFormat(jsdocContent: string): 'primitive' | 'table' | 'tree' | 'list' | 'none' | undefined {
818
- const match = jsdocContent.match(/@format\s+(primitive|table|tree|list|none)/i);
819
- if (match) {
820
- return match[1].toLowerCase() as 'primitive' | 'table' | 'tree' | 'list' | 'none';
819
+ private extractFormat(jsdocContent: string): OutputFormat | undefined {
820
+ // Match structural formats
821
+ const structuralMatch = jsdocContent.match(/@format\s+(primitive|table|tree|list|none)/i);
822
+ if (structuralMatch) {
823
+ return structuralMatch[1].toLowerCase() as OutputFormat;
821
824
  }
825
+
826
+ // Match content formats
827
+ const contentMatch = jsdocContent.match(/@format\s+(json|markdown|yaml|xml|html)/i);
828
+ if (contentMatch) {
829
+ return contentMatch[1].toLowerCase() as OutputFormat;
830
+ }
831
+
832
+ // Match code format (with optional language)
833
+ const codeMatch = jsdocContent.match(/@format\s+code(?::(\w+))?/i);
834
+ if (codeMatch) {
835
+ return codeMatch[1] ? `code:${codeMatch[1]}` as OutputFormat : 'code';
836
+ }
837
+
822
838
  return undefined;
823
839
  }
824
840
 
package/src/types.ts CHANGED
@@ -2,6 +2,16 @@
2
2
  * Photon MCP Core Types
3
3
  */
4
4
 
5
+ /**
6
+ * Output format types
7
+ * - Structural: primitive, table, tree, list, none
8
+ * - Content: json, markdown, yaml, xml, html, code, code:<lang>
9
+ */
10
+ export type OutputFormat =
11
+ | 'primitive' | 'table' | 'tree' | 'list' | 'none'
12
+ | 'json' | 'markdown' | 'yaml' | 'xml' | 'html'
13
+ | `code` | `code:${string}`;
14
+
5
15
  export interface PhotonTool {
6
16
  name: string;
7
17
  description: string;
@@ -10,6 +20,7 @@ export interface PhotonTool {
10
20
  properties: Record<string, any>;
11
21
  required?: string[];
12
22
  };
23
+ outputFormat?: OutputFormat;
13
24
  }
14
25
 
15
26
  export interface ExtractedSchema {
@@ -20,7 +31,7 @@ export interface ExtractedSchema {
20
31
  properties: Record<string, any>;
21
32
  required?: string[];
22
33
  };
23
- format?: 'primitive' | 'table' | 'tree' | 'list' | 'none';
34
+ outputFormat?: OutputFormat;
24
35
  }
25
36
 
26
37
  export interface PhotonMCPClass {