@porchestra/cli 1.0.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 (37) hide show
  1. package/README.md +625 -0
  2. package/bin/porchestra.js +2 -0
  3. package/package.json +51 -0
  4. package/src/agents/testPrompt/ast.json +71 -0
  5. package/src/agents/testPrompt/config.ts +18 -0
  6. package/src/agents/testPrompt/index.ts +64 -0
  7. package/src/agents/testPrompt/schemas.ts +45 -0
  8. package/src/agents/testPrompt/tools.ts +88 -0
  9. package/src/commands/agents.ts +173 -0
  10. package/src/commands/config.ts +97 -0
  11. package/src/commands/explore.ts +160 -0
  12. package/src/commands/login.ts +101 -0
  13. package/src/commands/logout.ts +52 -0
  14. package/src/commands/pull.ts +220 -0
  15. package/src/commands/status.ts +78 -0
  16. package/src/commands/whoami.ts +56 -0
  17. package/src/core/api/client.ts +133 -0
  18. package/src/core/auth/auth-service.ts +176 -0
  19. package/src/core/auth/token-manager.ts +47 -0
  20. package/src/core/config/config-manager.ts +107 -0
  21. package/src/core/config/config-schema.ts +56 -0
  22. package/src/core/config/project-tracker.ts +158 -0
  23. package/src/core/generators/code-generator.ts +329 -0
  24. package/src/core/generators/schema-generator.ts +59 -0
  25. package/src/index.ts +85 -0
  26. package/src/types/index.ts +214 -0
  27. package/src/utils/date.ts +23 -0
  28. package/src/utils/errors.ts +38 -0
  29. package/src/utils/logger.ts +11 -0
  30. package/src/utils/path-utils.ts +47 -0
  31. package/tests/unit/config-manager.test.ts +74 -0
  32. package/tests/unit/config-schema.test.ts +61 -0
  33. package/tests/unit/path-utils.test.ts +53 -0
  34. package/tests/unit/schema-generator.test.ts +82 -0
  35. package/tsconfig.json +30 -0
  36. package/tsup.config.ts +19 -0
  37. package/vitest.config.ts +20 -0
@@ -0,0 +1,71 @@
1
+ {
2
+ "root": {
3
+ "id": "EvcRFNShH3",
4
+ "type": "block",
5
+ "children": [
6
+ {
7
+ "id": "QgKHml42sL",
8
+ "type": "text",
9
+ "content": "system prompt test"
10
+ },
11
+ {
12
+ "id": "KRvYYUqy7J",
13
+ "type": "block",
14
+ "children": [
15
+ {
16
+ "id": "YlHnTwSkEi",
17
+ "else": {
18
+ "id": "i2pRTTZQlM",
19
+ "type": "block",
20
+ "children": [
21
+ {
22
+ "id": "Bd9VwLpfQO",
23
+ "type": "text",
24
+ "content": "val 1 not provided"
25
+ }
26
+ ]
27
+ },
28
+ "then": {
29
+ "id": "Z30x3K8DQG",
30
+ "type": "block",
31
+ "children": [
32
+ {
33
+ "id": "d1v8PEValw",
34
+ "type": "text",
35
+ "content": "val 1 provided"
36
+ }
37
+ ]
38
+ },
39
+ "type": "conditional",
40
+ "condition": {
41
+ "op": "eq",
42
+ "left": {
43
+ "path": "var",
44
+ "type": "variable"
45
+ },
46
+ "right": {
47
+ "type": "constant",
48
+ "value": "val1"
49
+ }
50
+ }
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ },
56
+ "version": "2.1.0",
57
+ "variables": [
58
+ {
59
+ "id": "mdMpwnrBKG",
60
+ "path": "var",
61
+ "type": "enum",
62
+ "validation": {
63
+ "options": [
64
+ "val1",
65
+ "val2"
66
+ ],
67
+ "required": true
68
+ }
69
+ }
70
+ ]
71
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Auto-generated model config for testPrompt
3
+ * Generated: 2026-02-01T11:57:10.473Z
4
+ * Version: 1.0.0
5
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
6
+ */
7
+
8
+ export const ModelConfig = {
9
+ "model": "gemini-3-flash-preview",
10
+ "provider": "GoogleGenAI",
11
+ "overrides": {},
12
+ "parameters": {
13
+ "thinking_config": {
14
+ "thinkingLevel": "LOW"
15
+ },
16
+ "response_mime_type": "application/json"
17
+ }
18
+ } as const;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Auto-generated Agent class for testPrompt
3
+ * Generated: 2026-02-01T11:57:10.473Z
4
+ * Version: 1.0.0
5
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
6
+ */
7
+
8
+ import { PorchestraRuntime } from '@porchestra/core';
9
+ import rawAst from './ast.json';
10
+ import { ModelConfig } from './config.js';
11
+ import * as Schemas from './schemas.js';
12
+ import * as Tools from './tools.js';
13
+
14
+ export class TestPromptAgent {
15
+ /**
16
+ * @param tools - The implementation of the tool logic. Required if the agent uses tools.
17
+ */
18
+ constructor(private readonly tools?: Tools.ToolImplementation) {}
19
+
20
+ /**
21
+ * Renders the System Prompt.
22
+ * Throws ZodError if variables are invalid.
23
+ */
24
+ public renderPrompt(variables: Schemas.AgentVariables): string {
25
+ const safeVars = Schemas.VariableSchema.parse(variables);
26
+ return PorchestraRuntime.render(rawAst as any, safeVars);
27
+ }
28
+
29
+ /**
30
+ * Routes an LLM Tool Call to your implementation.
31
+ * Validates arguments automatically.
32
+ */
33
+ public async handleTool(name: string, args: unknown): Promise<unknown> {
34
+ if (!this.tools) {
35
+ throw new Error('No tool implementation provided to Agent constructor.');
36
+ }
37
+ return Tools.dispatch(name, args, this.tools);
38
+ }
39
+
40
+ /** Validates User Input (e.g. from API Request) */
41
+ public static validateInput(data: unknown): Schemas.AgentInput {
42
+ return Schemas.InputSchema.parse(data);
43
+ }
44
+
45
+ /** Validates LLM Output (Post-generation check) */
46
+ public static validateResponse(data: unknown): Schemas.AgentResponse {
47
+ return Schemas.ResponseSchema.parse(data);
48
+ }
49
+
50
+ /** Get Provider Configuration */
51
+ public static get config() {
52
+ return ModelConfig;
53
+ }
54
+
55
+ /** Get Tool Definitions for the LLM Provider */
56
+ public static get toolDefinitions() {
57
+ return Tools.ToolDefinitions;
58
+ }
59
+
60
+ /** Get JSON Schema for Structured Outputs */
61
+ public static get responseFormat() {
62
+ return Schemas.ResponseJsonSchema;
63
+ }
64
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Auto-generated Zod schemas for testPrompt
3
+ * Generated: 2026-02-01T11:57:10.473Z
4
+ * Version: 1.0.0
5
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
6
+ */
7
+
8
+ import { z } from 'zod';
9
+
10
+ // 1. System Prompt Variables
11
+ export const VariableSchema = z.any();
12
+ export type AgentVariables = z.infer<typeof VariableSchema>;
13
+
14
+ // 2. User Input
15
+ export const InputSchema = z.object({
16
+ "user-age": z.number().min(18)
17
+ });
18
+ export type AgentInput = z.infer<typeof InputSchema>;
19
+
20
+ // 3. Model Response
21
+ export const ResponseSchema = z.object({
22
+ "is-allowed": z.any()
23
+ });
24
+ export type AgentResponse = z.infer<typeof ResponseSchema>;
25
+
26
+ // 4. Raw JSON Schema (for LLM Provider 'response_format')
27
+ export const ResponseJsonSchema = {
28
+ "type": "object",
29
+ "required": [
30
+ "is-allowed"
31
+ ],
32
+ "properties": {
33
+ "is-allowed": {
34
+ "enum": [
35
+ "yes",
36
+ "no"
37
+ ],
38
+ "type": "enum",
39
+ "description": ""
40
+ }
41
+ },
42
+ "propertyOrdering": [
43
+ "is-allowed"
44
+ ]
45
+ } as const;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Auto-generated tools for testPrompt
3
+ * Generated: 2026-02-01T11:57:10.473Z
4
+ * Version: 1.0.0
5
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
6
+ */
7
+
8
+ import { z } from 'zod';
9
+
10
+ // Schema for test-tool-1
11
+ export const TestTool1Params = z.object({
12
+ "test-param-1": z.string(),
13
+ "test-param-2": z.number().int()
14
+ });
15
+ export type TestTool1ParamsType = z.infer<typeof TestTool1Params>;
16
+
17
+ // Schema for test-tool-2
18
+ export const TestTool2Params = z.object({
19
+ "test-param-1": z.string()
20
+ });
21
+ export type TestTool2ParamsType = z.infer<typeof TestTool2Params>;
22
+
23
+ // The Interface the user MUST implement
24
+ export interface ToolImplementation {
25
+ testTool1: (args: TestTool1ParamsType) => Promise<unknown>;
26
+ testTool2: (args: TestTool2ParamsType) => Promise<unknown>;
27
+ }
28
+
29
+ // Raw Tool Definitions for provider SDKs
30
+ export const ToolDefinitions = [
31
+ {
32
+ name: 'test-tool-1',
33
+ description: "test tool",
34
+ parameters: {
35
+ "type": "object",
36
+ "required": [
37
+ "test-param-1",
38
+ "test-param-2"
39
+ ],
40
+ "properties": {
41
+ "test-param-1": {
42
+ "type": "string"
43
+ },
44
+ "test-param-2": {
45
+ "type": "integer"
46
+ }
47
+ },
48
+ "propertyOrdering": [
49
+ "test-param-1",
50
+ "test-param-2"
51
+ ]
52
+ },
53
+ },
54
+ {
55
+ name: 'test-tool-2',
56
+ description: "test tool 2",
57
+ parameters: {
58
+ "type": "object",
59
+ "required": [
60
+ "test-param-1"
61
+ ],
62
+ "properties": {
63
+ "test-param-1": {
64
+ "type": "string"
65
+ }
66
+ },
67
+ "propertyOrdering": [
68
+ "test-param-1"
69
+ ]
70
+ },
71
+ },
72
+ ] as const;
73
+
74
+ // Dispatcher with validation
75
+ export async function dispatch(
76
+ name: string,
77
+ args: unknown,
78
+ impl: ToolImplementation
79
+ ): Promise<unknown> {
80
+ switch (name) {
81
+ case 'test-tool-1':
82
+ return await impl.testTool1(TestTool1Params.parse(args));
83
+ case 'test-tool-2':
84
+ return await impl.testTool2(TestTool2Params.parse(args));
85
+ default:
86
+ throw new Error(`Unknown tool: ${name}`);
87
+ }
88
+ }
@@ -0,0 +1,173 @@
1
+ import { Command } from 'commander';
2
+ import { select, confirm } from '@inquirer/prompts';
3
+ import pc from 'picocolors';
4
+ import { ConfigManager } from '../core/config/config-manager.js';
5
+ import { ProjectTracker } from '../core/config/project-tracker.js';
6
+ import { formatDistanceToNow } from '../utils/date.js';
7
+
8
+ interface ListOptions {
9
+ project?: string;
10
+ json?: boolean;
11
+ }
12
+
13
+ interface RemoveOptions {
14
+ project?: string;
15
+ force?: boolean;
16
+ }
17
+
18
+ export function createAgentsCommand(configManager: ConfigManager): Command {
19
+ const projectTracker = new ProjectTracker(configManager);
20
+
21
+ const listAction = async (options: ListOptions = {}) => {
22
+ let projects = await projectTracker.getTrackedProjects();
23
+
24
+ if (options.project) {
25
+ projects = projects.filter(
26
+ project => project.projectId === options.project || project.projectSlug === options.project
27
+ );
28
+
29
+ if (projects.length === 0) {
30
+ console.log(pc.red(`No tracked project found for "${options.project}"`));
31
+ return;
32
+ }
33
+ }
34
+
35
+ if (options.json) {
36
+ console.log(JSON.stringify(projects, null, 2));
37
+ return;
38
+ }
39
+
40
+ console.log(pc.bold('\n📁 Tracked Agents\n'));
41
+
42
+ if (projects.length === 0) {
43
+ console.log(pc.gray(' No agents tracked. Run `porchestra explore` to add agents.'));
44
+ console.log();
45
+ return;
46
+ }
47
+
48
+ let totalAgents = 0;
49
+
50
+ projects.forEach((project) => {
51
+ totalAgents += project.agents.length;
52
+ console.log(pc.bold(` ${project.projectName}`));
53
+
54
+ project.agents.forEach((agent) => {
55
+ const lastPulled = agent.lastPulledAt
56
+ ? formatDistanceToNow(new Date(agent.lastPulledAt))
57
+ : 'pending';
58
+ const version = agent.lastPulledVersion ? ` @ ${agent.lastPulledVersion}` : '';
59
+
60
+ console.log(` └─ ${pc.cyan(agent.agentName)}${version ? pc.gray(version) : ''}`);
61
+ console.log(pc.gray(` ${agent.folderPath} (${lastPulled})`));
62
+ });
63
+
64
+ console.log();
65
+ });
66
+
67
+ console.log(pc.gray(`Total: ${projects.length} project(s), ${totalAgents} agent(s)\n`));
68
+ };
69
+
70
+ const command = new Command('agents')
71
+ .description('Manage tracked agents')
72
+ .action(async () => listAction());
73
+
74
+ command
75
+ .command('list')
76
+ .description('List tracked agents')
77
+ .option('-p, --project <id>', 'Filter by project id or slug')
78
+ .option('--json', 'Output raw JSON for scripting')
79
+ .action(listAction);
80
+
81
+ command
82
+ .command('remove [agent]')
83
+ .description('Stop tracking a specific agent')
84
+ .option('-p, --project <id>', 'Project id or slug (disambiguates agents with the same slug)')
85
+ .option('-f, --force', 'Skip confirmation prompt')
86
+ .action(async (agentArg: string | undefined, options: RemoveOptions) => {
87
+ let projects = await projectTracker.getTrackedProjects();
88
+
89
+ if (projects.length === 0) {
90
+ console.log(pc.gray('No agents tracked. Run `porchestra explore` first.'));
91
+ return;
92
+ }
93
+
94
+ if (options.project) {
95
+ projects = projects.filter(
96
+ project => project.projectId === options.project || project.projectSlug === options.project
97
+ );
98
+
99
+ if (projects.length === 0) {
100
+ console.log(pc.red(`No tracked project found for "${options.project}"`));
101
+ return;
102
+ }
103
+ }
104
+
105
+ const candidates = projects.flatMap((project) =>
106
+ project.agents.map((agent) => ({
107
+ project,
108
+ agent,
109
+ value: `${project.projectId}:${agent.agentId}`,
110
+ }))
111
+ );
112
+
113
+ let filtered = candidates;
114
+
115
+ if (agentArg) {
116
+ filtered = candidates.filter(({ agent }) =>
117
+ agent.agentId === agentArg || agent.agentSlug === agentArg
118
+ );
119
+ }
120
+
121
+ if (filtered.length === 0) {
122
+ const message = agentArg
123
+ ? `No tracked agent found for "${agentArg}".`
124
+ : 'No tracked agents found.';
125
+ console.log(pc.red(message));
126
+ return;
127
+ }
128
+
129
+ let target = filtered[0];
130
+
131
+ if (filtered.length > 1) {
132
+ const choice = await select({
133
+ message: 'Select an agent to stop tracking:',
134
+ choices: filtered.map(({ project, agent, value }) => ({
135
+ name: `${agent.agentName} ${pc.gray(`(${project.projectName}) ${agent.folderPath}`)}`,
136
+ value,
137
+ })),
138
+ });
139
+
140
+ target = filtered.find(candidate => candidate.value === choice)!;
141
+ }
142
+
143
+ if (!options.force) {
144
+ const confirmed = await confirm({
145
+ message: `Remove ${target.agent.agentName} from ${target.project.projectName}?`,
146
+ default: false,
147
+ });
148
+
149
+ if (!confirmed) return;
150
+ }
151
+
152
+ const removed = await projectTracker.untrackAgent(
153
+ target.project.projectId,
154
+ target.agent.agentId
155
+ );
156
+
157
+ if (!removed) {
158
+ console.log(pc.red('Agent was not removed (not found).'));
159
+ return;
160
+ }
161
+
162
+ const projectRemoved = target.project.agents.length === 1;
163
+
164
+ console.log(pc.green(`✓ Stopped tracking ${target.agent.agentName}`));
165
+ console.log(pc.gray(` Project: ${target.project.projectName}`));
166
+
167
+ if (projectRemoved) {
168
+ console.log(pc.gray(' Project removed from tracking because it has no remaining agents.'));
169
+ }
170
+ });
171
+
172
+ return command;
173
+ }
@@ -0,0 +1,97 @@
1
+ import { Command } from 'commander';
2
+ import { ConfigManager } from '../core/config/config-manager.js';
3
+ import pc from 'picocolors';
4
+ import { confirm } from '@inquirer/prompts';
5
+
6
+ export function createConfigCommand(configManager: ConfigManager): Command {
7
+ const config = new Command('config')
8
+ .description('Manage CLI configuration');
9
+
10
+ config
11
+ .command('get <key>')
12
+ .description('Get a configuration value')
13
+ .action(async (key: string) => {
14
+ const cfg = await configManager.get();
15
+ const value = getNestedValue(cfg, key);
16
+ console.log(value !== undefined ? value : pc.gray('(not set)'));
17
+ });
18
+
19
+ config
20
+ .command('set <key> <value>')
21
+ .description('Set a configuration value')
22
+ .action(async (key: string, value: string) => {
23
+ await configManager.update((cfg) => {
24
+ return setNestedValue(cfg, key, parseValue(value));
25
+ });
26
+ console.log(pc.green(`✓ Set ${key} = ${value}`));
27
+ });
28
+
29
+ config
30
+ .command('list')
31
+ .description('List all configuration values')
32
+ .action(async () => {
33
+ const cfg = await configManager.get();
34
+ console.log(pc.bold('\nConfiguration:'));
35
+ printConfig(cfg);
36
+ });
37
+
38
+ config
39
+ .command('reset')
40
+ .description('Reset all configuration (WARNING: clears auth)')
41
+ .option('--force', 'Skip confirmation')
42
+ .action(async (options) => {
43
+ if (!options.force) {
44
+ const confirmed = await confirm({
45
+ message: 'This will clear all config including login. Continue?',
46
+ default: false
47
+ });
48
+ if (!confirmed) return;
49
+ }
50
+ await configManager.clear();
51
+ console.log(pc.green('✓ Configuration reset'));
52
+ });
53
+
54
+ return config;
55
+ }
56
+
57
+ function getNestedValue(obj: any, key: string): any {
58
+ return key.split('.').reduce((o, k) => o?.[k], obj);
59
+ }
60
+
61
+ function setNestedValue(obj: any, key: string, value: any): any {
62
+ const keys = key.split('.');
63
+ const last = keys.pop()!;
64
+ const target = keys.reduce((o, k) => {
65
+ if (!o[k]) o[k] = {};
66
+ return o[k];
67
+ }, obj);
68
+ target[last] = value;
69
+ return obj;
70
+ }
71
+
72
+ function parseValue(value: string): any {
73
+ if (value === 'true') return true;
74
+ if (value === 'false') return false;
75
+ if (value === 'null') return null;
76
+ if (!isNaN(Number(value))) return Number(value);
77
+ try {
78
+ return JSON.parse(value);
79
+ } catch {
80
+ return value;
81
+ }
82
+ }
83
+
84
+ function printConfig(obj: any, prefix = ''): void {
85
+ for (const [key, value] of Object.entries(obj)) {
86
+ const fullKey = prefix ? `${prefix}.${key}` : key;
87
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
88
+ console.log(`\n${pc.cyan(fullKey)}:`);
89
+ printConfig(value, fullKey);
90
+ } else {
91
+ const displayValue = key.includes('token')
92
+ ? pc.gray('***hidden***')
93
+ : value;
94
+ console.log(` ${fullKey} = ${displayValue}`);
95
+ }
96
+ }
97
+ }