prompt-language-shell 0.4.0 → 0.4.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.
@@ -59,13 +59,13 @@ Present capabilities in two categories:
59
59
 
60
60
  These are the core operations available to all users:
61
61
 
62
- - **CONFIG**: Configuration changes, settings updates
63
- - **PLAN**: Plan and structure tasks from natural language requests, breaking
62
+ - **Config**: Configuration changes, settings updates
63
+ - **Plan**: Plan and structure tasks from natural language requests, breaking
64
64
  them down into clear, actionable steps
65
- - **INTROSPECT**: List and describe available capabilities and skills
66
- - **ANSWER**: Answer questions, explain concepts, provide information
67
- - **EXECUTE**: Run shell commands, execute programs, process operations
68
- - **REPORT**: Generate summaries, create reports, display results
65
+ - **Introspect**: List and describe available capabilities and skills
66
+ - **Answer**: Answer questions, explain concepts, provide information
67
+ - **Execute**: Run shell commands, execute programs, process operations
68
+ - **Report**: Generate summaries, create reports, display results
69
69
 
70
70
  ### 2. User-Defined Skills
71
71
 
@@ -82,15 +82,20 @@ in the response. For each skill:
82
82
  Create tasks with type "introspect" for each capability. Each task should:
83
83
 
84
84
  - **Action**: The capability name and a concise description
85
- - Format: "CAPABILITY: Description"
85
+ - Format: "Capability Name: description" (note: display format will use " - " separator)
86
+ - **IMPORTANT**: Use title case for capability names (e.g., "Plan", "Execute"), NOT all uppercase (NOT "PLAN", "EXECUTE")
86
87
  - Examples:
87
- - "PLAN: Break down requests into actionable steps"
88
- - "EXECUTE: Run shell commands and process operations"
89
- - "Deploy Application: Build and deploy to staging or production"
88
+ - "Plan: break down requests into actionable steps"
89
+ - "Execute: run shell commands and process operations"
90
+ - "Deploy Application: build and deploy to staging or production"
90
91
  - **Type**: Always use "introspect"
91
92
  - **Params**: Omit params field
92
93
 
93
- **Keep action descriptions concise, at most 64 characters.**
94
+ **Keep action descriptions concise:**
95
+ - Maximum 60 characters for the description portion (after the colon)
96
+ - Focus on clarity and brevity
97
+ - Describe the core purpose in one short phrase
98
+ - Start descriptions with a lowercase letter (they follow a colon)
94
99
 
95
100
  ## Filtering
96
101
 
@@ -111,8 +116,8 @@ Examples:
111
116
  ### Example 1: List All Capabilities
112
117
 
113
118
  When user asks "list your skills", create an introductory message like "here
114
- are my capabilities:" followed by a task for each built-in capability: PLAN,
115
- INTROSPECT, ANSWER, EXECUTE, REPORT, and CONFIG. Each task uses type
119
+ are my capabilities:" followed by a task for each built-in capability: Plan,
120
+ Introspect, Answer, Execute, Report, and Config. Each task uses type
116
121
  "introspect" with an action describing the capability.
117
122
 
118
123
  ### Example 2: Filtered Skills
@@ -237,7 +237,7 @@ Examples that should be aborted as offensive:
237
237
  capabilities or skills:
238
238
  - Verbs: "list skills", "show skills", "what can you do", "list
239
239
  capabilities", "show capabilities", "what skills", "describe skills",
240
- "flex", "show off"
240
+ "introspect", "flex", "show off"
241
241
  - **Filtering**: If the request specifies a category, domain, or context
242
242
  (e.g., "for deployment", "related to files", "about testing"), add a
243
243
  params object with a filter field containing the specified context
@@ -125,6 +125,33 @@ export function createConfirmDefinition(onConfirmed, onCancelled) {
125
125
  },
126
126
  };
127
127
  }
128
+ export function createIntrospectDefinition(tasks, service, onError, onComplete, onAborted) {
129
+ return {
130
+ id: randomUUID(),
131
+ name: ComponentName.Introspect,
132
+ state: {
133
+ done: false,
134
+ isLoading: true,
135
+ },
136
+ props: {
137
+ tasks,
138
+ service,
139
+ onError,
140
+ onComplete,
141
+ onAborted,
142
+ },
143
+ };
144
+ }
145
+ export function createReportDefinition(message, capabilities) {
146
+ return {
147
+ id: randomUUID(),
148
+ name: ComponentName.Report,
149
+ props: {
150
+ message,
151
+ capabilities,
152
+ },
153
+ };
154
+ }
128
155
  export function isStateless(component) {
129
156
  return !('state' in component);
130
157
  }
@@ -8,6 +8,8 @@ export var ComponentName;
8
8
  ComponentName["Refinement"] = "refinement";
9
9
  ComponentName["Feedback"] = "feedback";
10
10
  ComponentName["Confirm"] = "confirm";
11
+ ComponentName["Introspect"] = "introspect";
12
+ ComponentName["Report"] = "report";
11
13
  })(ComponentName || (ComponentName = {}));
12
14
  export var TaskType;
13
15
  (function (TaskType) {
@@ -5,9 +5,11 @@ import { Command } from './Command.js';
5
5
  import { Confirm } from './Confirm.js';
6
6
  import { Config } from './Config.js';
7
7
  import { Feedback } from './Feedback.js';
8
+ import { Introspect } from './Introspect.js';
8
9
  import { Message } from './Message.js';
9
10
  import { Plan } from './Plan.js';
10
11
  import { Refinement } from './Refinement.js';
12
+ import { Report } from './Report.js';
11
13
  import { Welcome } from './Welcome.js';
12
14
  export const Component = React.memo(function Component({ def, debug, }) {
13
15
  switch (def.name) {
@@ -39,5 +41,12 @@ export const Component = React.memo(function Component({ def, debug, }) {
39
41
  const state = def.state;
40
42
  return _jsx(Confirm, { ...props, state: state });
41
43
  }
44
+ case ComponentName.Introspect: {
45
+ const props = def.props;
46
+ const state = def.state;
47
+ return _jsx(Introspect, { ...props, state: state });
48
+ }
49
+ case ComponentName.Report:
50
+ return _jsx(Report, { ...def.props });
42
51
  }
43
52
  });
@@ -0,0 +1,98 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ import { Spinner } from './Spinner.js';
5
+ const MIN_PROCESSING_TIME = 1000;
6
+ const BUILT_IN_CAPABILITIES = new Set([
7
+ 'CONFIG',
8
+ 'PLAN',
9
+ 'INTROSPECT',
10
+ 'ANSWER',
11
+ 'EXECUTE',
12
+ 'REPORT',
13
+ ]);
14
+ function parseCapabilityFromTask(task) {
15
+ // Parse "NAME: Description" format from task.action
16
+ const colonIndex = task.action.indexOf(':');
17
+ if (colonIndex === -1) {
18
+ return {
19
+ name: task.action,
20
+ description: '',
21
+ isBuiltIn: BUILT_IN_CAPABILITIES.has(task.action.toUpperCase()),
22
+ };
23
+ }
24
+ const name = task.action.substring(0, colonIndex).trim();
25
+ const description = task.action.substring(colonIndex + 1).trim();
26
+ const isBuiltIn = BUILT_IN_CAPABILITIES.has(name.toUpperCase());
27
+ return {
28
+ name,
29
+ description,
30
+ isBuiltIn,
31
+ };
32
+ }
33
+ export function Introspect({ tasks, state, service, children, onError, onComplete, onAborted, }) {
34
+ const done = state?.done ?? false;
35
+ const [error, setError] = useState(null);
36
+ const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
37
+ useInput((input, key) => {
38
+ if (key.escape && isLoading && !done) {
39
+ setIsLoading(false);
40
+ onAborted();
41
+ }
42
+ }, { isActive: isLoading && !done });
43
+ useEffect(() => {
44
+ // Skip processing if done
45
+ if (done) {
46
+ return;
47
+ }
48
+ // Skip processing if no service available
49
+ if (!service) {
50
+ setError('No service available');
51
+ setIsLoading(false);
52
+ return;
53
+ }
54
+ let mounted = true;
55
+ async function process(svc) {
56
+ const startTime = Date.now();
57
+ try {
58
+ // Get the introspect task action (first task should have the intro message)
59
+ const introspectAction = tasks[0]?.action || 'list capabilities';
60
+ // Call introspect tool
61
+ const result = await svc.processWithTool(introspectAction, 'introspect');
62
+ const elapsed = Date.now() - startTime;
63
+ const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
64
+ await new Promise((resolve) => setTimeout(resolve, remainingTime));
65
+ if (mounted) {
66
+ // Parse capabilities from returned tasks
67
+ const capabilities = result.tasks.map(parseCapabilityFromTask);
68
+ setIsLoading(false);
69
+ onComplete?.(result.message, capabilities);
70
+ }
71
+ }
72
+ catch (err) {
73
+ const elapsed = Date.now() - startTime;
74
+ const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
75
+ await new Promise((resolve) => setTimeout(resolve, remainingTime));
76
+ if (mounted) {
77
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
78
+ setIsLoading(false);
79
+ if (onError) {
80
+ onError(errorMessage);
81
+ }
82
+ else {
83
+ setError(errorMessage);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ process(service);
89
+ return () => {
90
+ mounted = false;
91
+ };
92
+ }, [tasks, done, service]);
93
+ // Don't render wrapper when done and nothing to show
94
+ if (!isLoading && !error && !children) {
95
+ return null;
96
+ }
97
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { children: "Listing capabilities. " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) })), children] }));
98
+ }
package/dist/ui/Main.js CHANGED
@@ -5,7 +5,7 @@ import { ComponentName, FeedbackType, TaskType, } from '../types/types.js';
5
5
  import { createAnthropicService, } from '../services/anthropic.js';
6
6
  import { getConfigurationRequiredMessage, hasValidAnthropicKey, loadConfig, loadDebugSetting, saveAnthropicConfig, saveDebugSetting, } from '../services/config.js';
7
7
  import { FeedbackMessages, getCancellationMessage, getRefiningMessage, } from '../services/messages.js';
8
- import { createCommandDefinition, createConfirmDefinition, createConfigDefinition, createFeedback, createMessage, createRefinement, createPlanDefinition, createWelcomeDefinition, isStateless, markAsDone, } from '../services/components.js';
8
+ import { createCommandDefinition, createConfirmDefinition, createConfigDefinition, createFeedback, createIntrospectDefinition, createMessage, createPlanDefinition, createRefinement, createReportDefinition, createWelcomeDefinition, isStateless, markAsDone, } from '../services/components.js';
9
9
  import { exitApp } from '../services/process.js';
10
10
  import { Column } from './Column.js';
11
11
  export const Main = ({ app, command }) => {
@@ -95,18 +95,82 @@ export const Main = ({ app, command }) => {
95
95
  const handleRefinementAborted = React.useCallback(() => {
96
96
  handleAborted('Plan refinement');
97
97
  }, [handleAborted]);
98
+ const handleIntrospectAborted = React.useCallback(() => {
99
+ handleAborted('Introspection');
100
+ }, [handleAborted]);
101
+ const handleIntrospectError = React.useCallback((error) => {
102
+ setQueue((currentQueue) => {
103
+ if (currentQueue.length === 0)
104
+ return currentQueue;
105
+ const [first] = currentQueue;
106
+ if (first.name === ComponentName.Introspect) {
107
+ addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, FeedbackMessages.UnexpectedError, error));
108
+ }
109
+ exitApp(1);
110
+ return [];
111
+ });
112
+ }, [addToTimeline]);
113
+ const handleIntrospectComplete = React.useCallback((message, capabilities) => {
114
+ setQueue((currentQueue) => {
115
+ if (currentQueue.length === 0)
116
+ return currentQueue;
117
+ const [first] = currentQueue;
118
+ if (first.name === ComponentName.Introspect) {
119
+ // Don't add the Introspect component to timeline (it renders null)
120
+ // Only add the Report component
121
+ addToTimeline(createReportDefinition(message, capabilities));
122
+ }
123
+ exitApp(0);
124
+ return [];
125
+ });
126
+ }, [addToTimeline]);
98
127
  const handleExecutionConfirmed = React.useCallback(() => {
99
128
  setQueue((currentQueue) => {
100
129
  if (currentQueue.length === 0)
101
130
  return currentQueue;
102
131
  const [first] = currentQueue;
103
132
  if (first.name === ComponentName.Confirm) {
104
- addToTimeline(markAsDone(first));
133
+ // Find the most recent Plan in timeline to get tasks
134
+ const currentTimeline = timelineRef.current;
135
+ const lastPlanIndex = [...currentTimeline]
136
+ .reverse()
137
+ .findIndex((item) => item.name === ComponentName.Plan);
138
+ const lastPlan = lastPlanIndex >= 0
139
+ ? currentTimeline[currentTimeline.length - 1 - lastPlanIndex]
140
+ : null;
141
+ const tasks = lastPlan &&
142
+ lastPlan.name === ComponentName.Plan &&
143
+ 'props' in lastPlan &&
144
+ lastPlan.props &&
145
+ 'tasks' in lastPlan.props &&
146
+ Array.isArray(lastPlan.props.tasks)
147
+ ? lastPlan.props.tasks
148
+ : [];
149
+ const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
150
+ if (allIntrospect && tasks.length > 0) {
151
+ // Execute introspection
152
+ addToTimeline(markAsDone(first));
153
+ return [
154
+ createIntrospectDefinition(tasks, service, handleIntrospectError, handleIntrospectComplete, handleIntrospectAborted),
155
+ ];
156
+ }
157
+ else {
158
+ // Regular execution - just exit for now
159
+ addToTimeline(markAsDone(first));
160
+ exitApp(0);
161
+ return [];
162
+ }
105
163
  }
106
164
  exitApp(0);
107
165
  return [];
108
166
  });
109
- }, [addToTimeline]);
167
+ }, [
168
+ addToTimeline,
169
+ service,
170
+ handleIntrospectError,
171
+ handleIntrospectComplete,
172
+ handleIntrospectAborted,
173
+ ]);
110
174
  const handleExecutionCancelled = React.useCallback(() => {
111
175
  setQueue((currentQueue) => {
112
176
  if (currentQueue.length === 0)
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ const COLORS = {
4
+ BuiltIn: '#5c9ccc', // blue - for built-in capabilities
5
+ UserDefined: '#5aaa8a', // green - for user-defined skills
6
+ };
7
+ function CapabilityItem({ name, description, isBuiltIn }) {
8
+ const color = isBuiltIn ? COLORS.BuiltIn : COLORS.UserDefined;
9
+ return (_jsxs(Box, { children: [_jsx(Text, { children: "- " }), _jsx(Text, { color: color, children: name }), _jsxs(Text, { children: [" - ", description] })] }));
10
+ }
11
+ export function Report({ message, capabilities }) {
12
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: message }), _jsx(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: capabilities.map((capability, index) => (_jsx(CapabilityItem, { name: capability.name, description: capability.description, isBuiltIn: capability.isBuiltIn }, index))) })] }));
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-language-shell",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Your personal command-line concierge. Ask politely, and it gets things done.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",