@hasna/terminal 0.5.1 → 0.5.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.
package/dist/App.js CHANGED
@@ -69,7 +69,7 @@ export default function App() {
69
69
  const [activeTab, setActiveTab] = useState(0);
70
70
  const abortRef = useRef(null);
71
71
  let nextTabId = useRef(2);
72
- const sessionIdRef = useRef(createSession(process.cwd()));
72
+ const sessionIdRef = useRef("");
73
73
  const interactionIdRef = useRef(0);
74
74
  const tab = tabs[activeTab];
75
75
  const allNl = [...nlHistory, ...tab.sessionNl];
@@ -158,6 +158,10 @@ export default function App() {
158
158
  // ── translate + run ─────────────────────────────────────────────────────────
159
159
  const translateAndRun = async (nl, raw) => {
160
160
  updateTab(t => ({ ...t, sessionNl: [...t.sessionNl, nl] }));
161
+ // Lazy session creation — only when user actually types something
162
+ if (!sessionIdRef.current) {
163
+ sessionIdRef.current = createSession(process.cwd());
164
+ }
161
165
  // Log interaction start
162
166
  const startTime = Date.now();
163
167
  interactionIdRef.current = logInteraction(sessionIdRef.current, { nl });
@@ -45,6 +45,20 @@ export function getRecipe(name, projectPath) {
45
45
  export function createRecipe(opts) {
46
46
  const filePath = opts.project ? projectFile(opts.project) : GLOBAL_FILE;
47
47
  const store = loadStore(filePath);
48
+ // Prevent duplicates — update existing if same name
49
+ const existingIdx = store.recipes.findIndex(r => r.name === opts.name);
50
+ if (existingIdx >= 0) {
51
+ store.recipes[existingIdx].command = opts.command;
52
+ store.recipes[existingIdx].updatedAt = Date.now();
53
+ if (opts.description)
54
+ store.recipes[existingIdx].description = opts.description;
55
+ if (opts.tags)
56
+ store.recipes[existingIdx].tags = opts.tags;
57
+ if (opts.collection)
58
+ store.recipes[existingIdx].collection = opts.collection;
59
+ saveStore(filePath, store);
60
+ return store.recipes[existingIdx];
61
+ }
48
62
  // Auto-detect variables from command if not explicitly provided
49
63
  const detectedVars = extractVariables(opts.command);
50
64
  const variables = opts.variables ?? detectedVars.map(name => ({ name, required: true }));
@@ -98,6 +112,10 @@ export function listCollections(projectPath) {
98
112
  export function createCollection(opts) {
99
113
  const filePath = opts.project ? projectFile(opts.project) : GLOBAL_FILE;
100
114
  const store = loadStore(filePath);
115
+ // Prevent duplicates — return existing if same name
116
+ const existing = store.collections.find(c => c.name === opts.name);
117
+ if (existing)
118
+ return existing;
101
119
  const collection = {
102
120
  id: genId(),
103
121
  name: opts.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Smart terminal wrapper for AI agents and humans — structured output, token compression, MCP server, natural language",
5
5
  "type": "module",
6
6
  "bin": {
package/src/App.tsx CHANGED
@@ -110,7 +110,7 @@ export default function App() {
110
110
  const [activeTab, setActiveTab] = useState(0);
111
111
  const abortRef = useRef<AbortController | null>(null);
112
112
  let nextTabId = useRef(2);
113
- const sessionIdRef = useRef<string>(createSession(process.cwd()));
113
+ const sessionIdRef = useRef<string>("");
114
114
  const interactionIdRef = useRef<number>(0);
115
115
 
116
116
  const tab = tabs[activeTab];
@@ -217,6 +217,10 @@ export default function App() {
217
217
  const translateAndRun = async (nl: string, raw: boolean) => {
218
218
  updateTab(t => ({ ...t, sessionNl: [...t.sessionNl, nl] }));
219
219
 
220
+ // Lazy session creation — only when user actually types something
221
+ if (!sessionIdRef.current) {
222
+ sessionIdRef.current = createSession(process.cwd());
223
+ }
220
224
  // Log interaction start
221
225
  const startTime = Date.now();
222
226
  interactionIdRef.current = logInteraction(sessionIdRef.current, { nl });
@@ -61,6 +61,18 @@ export function createRecipe(opts: {
61
61
  const filePath = opts.project ? projectFile(opts.project) : GLOBAL_FILE;
62
62
  const store = loadStore(filePath);
63
63
 
64
+ // Prevent duplicates — update existing if same name
65
+ const existingIdx = store.recipes.findIndex(r => r.name === opts.name);
66
+ if (existingIdx >= 0) {
67
+ store.recipes[existingIdx].command = opts.command;
68
+ store.recipes[existingIdx].updatedAt = Date.now();
69
+ if (opts.description) store.recipes[existingIdx].description = opts.description;
70
+ if (opts.tags) store.recipes[existingIdx].tags = opts.tags;
71
+ if (opts.collection) store.recipes[existingIdx].collection = opts.collection;
72
+ saveStore(filePath, store);
73
+ return store.recipes[existingIdx];
74
+ }
75
+
64
76
  // Auto-detect variables from command if not explicitly provided
65
77
  const detectedVars = extractVariables(opts.command);
66
78
  const variables = opts.variables ?? detectedVars.map(name => ({ name, required: true }));
@@ -120,6 +132,10 @@ export function createCollection(opts: { name: string; description?: string; pro
120
132
  const filePath = opts.project ? projectFile(opts.project) : GLOBAL_FILE;
121
133
  const store = loadStore(filePath);
122
134
 
135
+ // Prevent duplicates — return existing if same name
136
+ const existing = store.collections.find(c => c.name === opts.name);
137
+ if (existing) return existing;
138
+
123
139
  const collection: Collection = {
124
140
  id: genId(),
125
141
  name: opts.name,