@unscientificjszhai/howto 1.0.0-alpha.3 → 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.
package/README.CN.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  _在终端中用 AI 快速找到可执行命令。_
4
4
 
5
- ![npm version](https://img.shields.io/npm/v/@unscientificjszhai/howto/latest)
5
+ [![npm version](https://img.shields.io/npm/v/@unscientificjszhai/howto/latest)](https://www.npmjs.com/package/@unscientificjszhai/howto)
6
+ [![GitHub Actions Test Status](https://github.com/UnscientificJsZhai/HowTo/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/UnscientificJsZhai/HowTo/actions)
7
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
+ [![License](https://img.shields.io/github/license/UnscientificJsZhai/HowTo)](LICENSE)
6
9
 
7
10
  [English](README.md) | 简体中文
8
11
 
@@ -148,7 +151,7 @@ howto --print "show current branch"
148
151
  - 响应包含一到三个候选项;
149
152
  - 所有占位符使用 `{{name}}` 语法,并且声明与引用一致;
150
153
  - `use <command>` 候选项在保守处理前缀后,明确以指定工具开头;
151
- - 明显危险的命令需要输入 `EXECUTE` 才能继续执行。
154
+ - 明显危险的命令需要输入 `EXECUTE` 才能继续执行;大小写不敏感。
152
155
 
153
156
  危险命令检测当前覆盖递归破坏性 `rm`、磁盘和文件系统操作、大范围递归权限变更、下载脚本后直接交给 shell 执行、高影响包管理器操作以及服务变更等高风险模式。
154
157
 
package/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  _Use AI to quickly find commands within the terminal._
4
4
 
5
- ![npm version](https://img.shields.io/npm/v/@unscientificjszhai/howto/latest)
5
+ [![npm version](https://img.shields.io/npm/v/@unscientificjszhai/howto/latest)](https://www.npmjs.com/package/@unscientificjszhai/howto)
6
+ [![GitHub Actions Test Status](https://github.com/UnscientificJsZhai/HowTo/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/UnscientificJsZhai/HowTo/actions)
7
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
+ [![License](https://img.shields.io/github/license/UnscientificJsZhai/HowTo)](LICENSE)
6
9
 
7
10
  English | [简体中文](README.CN.md)
8
11
 
@@ -148,7 +151,7 @@ howto --print "show current branch"
148
151
  - the response contains between one and three candidates;
149
152
  - all placeholders use `{{name}}` syntax and are declared consistently;
150
153
  - `use <command>` candidates clearly start with the requested tool after conservative prefix handling;
151
- - obvious dangerous patterns require typing `EXECUTE` before they can run.
154
+ - obvious dangerous patterns require typing `EXECUTE` before they can run; matching is case-insensitive.
152
155
 
153
156
  Dangerous-command detection currently covers high-risk patterns such as recursive destructive `rm`, disk and filesystem operations, broad recursive permission changes, downloaded scripts piped into a shell, high-impact package manager operations, and service changes.
154
157
 
@@ -0,0 +1,17 @@
1
+ # Third-Party Notices
2
+
3
+ This project depends on third-party packages that are installed by npm as separate packages.
4
+ Each dependency is distributed under its own license.
5
+
6
+ ## Runtime Dependencies
7
+
8
+ | Package | Version | License |
9
+ | --------------- | -------- | ---------- |
10
+ | `@google/genai` | `2.6.0` | Apache-2.0 |
11
+ | `ink` | `7.0.4` | MIT |
12
+ | `openai` | `6.39.0` | Apache-2.0 |
13
+ | `react` | `19.2.6` | MIT |
14
+
15
+ The package versions above reflect the installed dependency set at the time this notice
16
+ was prepared. See each package's published npm artifact for its complete license text and
17
+ any package-specific notices.
@@ -5,6 +5,8 @@ export const COMMAND_GENERATION_SCHEMA = {
5
5
  properties: {
6
6
  commands: {
7
7
  type: "array",
8
+ minItems: 1,
9
+ maxItems: 3,
8
10
  items: {
9
11
  type: "object",
10
12
  additionalProperties: false,
package/dist/index.js CHANGED
@@ -17,8 +17,8 @@ import { ConfigError, hasExplicitAiProvider, loadConfig } from "./config.js";
17
17
  import { readUserConfigFile } from "./config-file.js";
18
18
  import { createCommandProvider } from "./ai/index.js";
19
19
  import { buildCommandGenerationPrompt, createProviderPromptRequest } from "./prompt.js";
20
- import { parseAndValidateAiResponse } from "./validation/ai-response.js";
21
20
  import { checkCommandInPath } from "./validation/command-tool.js";
21
+ import { generateValidatedCommandCandidates } from "./validation/generated-commands.js";
22
22
  import { ensureInteractiveTty } from "./ui/tty.js";
23
23
  import { executeCommand } from "./execute.js";
24
24
  import { toAppError } from "./errors.js";
@@ -71,10 +71,9 @@ function run(argv) {
71
71
  const { systemPrompt, userPrompt } = buildCommandGenerationPrompt(promptRequest);
72
72
  const provider = createCommandProvider(config);
73
73
  if (parsedCli.options.print) {
74
- const aiResult = yield provider.generateCommands(Object.assign(Object.assign({}, promptRequest), { systemPrompt,
74
+ const candidates = yield generateValidatedCommandCandidates(provider, Object.assign(Object.assign({}, promptRequest), { systemPrompt,
75
75
  userPrompt }));
76
- const aiResponse = parseAndValidateAiResponse(aiResult.rawText);
77
- aiResponse.commands.forEach((candidate) => {
76
+ candidates.forEach((candidate) => {
78
77
  console.log(candidate.command);
79
78
  });
80
79
  return { exitCode: 0 };
@@ -84,7 +83,7 @@ function run(argv) {
84
83
  }
85
84
  let finalCommand;
86
85
  let appError;
87
- const { waitUntilExit, unmount, clear } = render(React.createElement(App, { provider: provider, request: Object.assign(Object.assign({}, promptRequest), { systemPrompt, userPrompt }), useCommand: parsedCli.useCommand, onSuccess: (command) => {
86
+ const { waitUntilExit, unmount, clear } = render(React.createElement(App, { provider: provider, request: Object.assign(Object.assign({}, promptRequest), { systemPrompt, userPrompt }), onSuccess: (command) => {
88
87
  finalCommand = command;
89
88
  clear();
90
89
  unmount();
package/dist/prompt.js CHANGED
@@ -19,6 +19,7 @@ The commands array must contain 1 to 3 items. Do not return more than 3 candidat
19
19
  Detect the primary natural language of the user's question. Return title, description, and placeholders[].description in that language. Keep placeholder name values English-compatible ASCII using only letters, numbers, underscores, or hyphens.
20
20
  Use placeholders in commands only as {{name}}, and declare every placeholder in the placeholders array. User may provide argument. If the user's intent is clear, try to use the provided arguments as parameters in the generated commands instead of placeholders. If the intent is unclear, do not fill them.`;
21
21
  export const STRUCTURED_OUTPUT_CONTRACT = `Return only the JSON object requested by the response schema. Do not include a natural-language body, markdown, or code fences.
22
+ The commands array must contain 1 to 3 items. Do not return more than 3 candidate commands.
22
23
  Detect the primary natural language of the user's question. Return title, description, and placeholders[].description in that language. Keep placeholder name values English-compatible ASCII using only letters, numbers, underscores, or hyphens.
23
24
  Use placeholders in commands only as {{name}}, and declare every placeholder in the placeholders array. User may provide argument. If the user's intent is clear, try to use the provided arguments as parameters in the generated commands instead of placeholders. If the intent is unclear, do not fill them.`;
24
25
  export const SAFETY_CONSTRAINTS = `Prefer read-only, reversible, and low-risk commands.
package/dist/ui/App.js CHANGED
@@ -4,12 +4,11 @@ import { LoadingView } from "./LoadingView.js";
4
4
  import { SelectCommandView } from "./SelectCommandView.js";
5
5
  import { ResolvePlaceholdersView } from "./ResolvePlaceholdersView.js";
6
6
  import { ConfirmView } from "./ConfirmView.js";
7
- import { parseAndValidateAiResponse } from "../validation/ai-response.js";
8
- import { validateUseCommandCandidates } from "../validation/command-tool.js";
7
+ import { generateValidatedCommandCandidates } from "../validation/generated-commands.js";
9
8
  import { detectDangerousCommand } from "../safety/dangerous-command.js";
10
- import { replaceCommandPlaceholders } from "./placeholder-logic.js";
9
+ import { resolveCandidatePlaceholders } from "./placeholder-logic.js";
11
10
  import { InteractionCancelledError } from "./tty.js";
12
- export const App = ({ provider, request, useCommand, onSuccess, onError }) => {
11
+ export const App = ({ provider, request, onSuccess, onError }) => {
13
12
  const { rows } = useWindowSize();
14
13
  const [status, setStatus] = useState("loading");
15
14
  const [candidates, setCandidates] = useState([]);
@@ -20,12 +19,9 @@ export const App = ({ provider, request, useCommand, onSuccess, onError }) => {
20
19
  const [errorMessage, setErrorMessage] = useState("");
21
20
  useEffect(() => {
22
21
  if (status === "loading") {
23
- provider
24
- .generateCommands(request)
25
- .then((result) => {
26
- const aiResponse = parseAndValidateAiResponse(result.rawText);
27
- validateUseCommandCandidates(aiResponse, useCommand);
28
- setCandidates(aiResponse.commands);
22
+ generateValidatedCommandCandidates(provider, request)
23
+ .then((generatedCandidates) => {
24
+ setCandidates(generatedCandidates);
29
25
  setStatus("selecting");
30
26
  })
31
27
  .catch((error) => {
@@ -35,7 +31,7 @@ export const App = ({ provider, request, useCommand, onSuccess, onError }) => {
35
31
  onError(appError);
36
32
  });
37
33
  }
38
- }, [status, provider, request, useCommand, onError]);
34
+ }, [status, provider, request, onError]);
39
35
  const handleSelect = (candidate) => {
40
36
  setSelectedCandidate(candidate);
41
37
  setResolvedValues(new Map());
@@ -43,19 +39,27 @@ export const App = ({ provider, request, useCommand, onSuccess, onError }) => {
43
39
  setStatus("resolving");
44
40
  }
45
41
  else {
46
- const command = candidate.command;
47
- setFinalCommand(command);
48
- setDanger(detectDangerousCommand(command));
49
- setStatus("confirming");
42
+ try {
43
+ const resolved = resolveCandidatePlaceholders(candidate, new Map());
44
+ setFinalCommand(resolved.command);
45
+ setResolvedValues(resolved.values);
46
+ setDanger(detectDangerousCommand(resolved.command));
47
+ setStatus("confirming");
48
+ }
49
+ catch (error) {
50
+ const appError = normalizeError(error);
51
+ setErrorMessage(appError.message);
52
+ setStatus("error");
53
+ onError(appError);
54
+ }
50
55
  }
51
56
  };
52
- const handleResolve = (values) => {
57
+ const handleResolve = (resolved) => {
53
58
  if (!selectedCandidate)
54
59
  return;
55
- setResolvedValues(values);
56
- const command = replaceCommandPlaceholders(selectedCandidate.command, values);
57
- setFinalCommand(command);
58
- setDanger(detectDangerousCommand(command));
60
+ setResolvedValues(resolved.values);
61
+ setFinalCommand(resolved.command);
62
+ setDanger(detectDangerousCommand(resolved.command));
59
63
  setStatus("confirming");
60
64
  };
61
65
  useEffect(() => {
@@ -76,8 +80,8 @@ export const App = ({ provider, request, useCommand, onSuccess, onError }) => {
76
80
  return (React.createElement(Box, { flexDirection: "column", maxHeight: maxFrameRows, overflowY: "hidden" },
77
81
  status === "loading" && React.createElement(LoadingView, null),
78
82
  status === "selecting" && (React.createElement(SelectCommandView, { candidates: candidates, onSelect: handleSelect, onCancel: handleCancel })),
79
- status === "resolving" && selectedCandidate && (React.createElement(ResolvePlaceholdersView, { candidate: selectedCandidate, placeholders: selectedCandidate.placeholders, onResolve: handleResolve, onBack: handleBackToSelection, onCancel: handleCancel })),
80
- (status === "confirming" || status === "done") && selectedCandidate && (React.createElement(ConfirmView, { candidate: selectedCandidate, resolvedValues: resolvedValues, danger: danger, onConfirm: handleConfirm, onCancel: handleCancel, isDone: status === "done" })),
83
+ status === "resolving" && selectedCandidate && (React.createElement(ResolvePlaceholdersView, { candidate: selectedCandidate, onResolve: handleResolve, onBack: handleBackToSelection, onCancel: handleCancel })),
84
+ (status === "confirming" || status === "done") && selectedCandidate && (React.createElement(ConfirmView, { candidate: selectedCandidate, command: finalCommand, resolvedValues: resolvedValues, danger: danger, onConfirm: handleConfirm, onCancel: handleCancel, isDone: status === "done" })),
81
85
  status === "error" && React.createElement(Text, { color: "red" },
82
86
  "Error: ",
83
87
  errorMessage)));
@@ -1,7 +1,10 @@
1
1
  import React, { useState } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import { SelectedCommandDisplay } from "./SelectedCommandDisplay.js";
4
- export const ConfirmView = ({ candidate, resolvedValues, danger, onConfirm, onCancel, isDone = false, }) => {
4
+ export function isDangerConfirmationInput(input) {
5
+ return input.toUpperCase() === "EXECUTE";
6
+ }
7
+ export const ConfirmView = ({ candidate, command, resolvedValues, danger, onConfirm, onCancel, isDone = false, }) => {
5
8
  const [buffer, setBuffer] = useState("");
6
9
  useInput((input, key) => {
7
10
  if (isDone)
@@ -16,7 +19,7 @@ export const ConfirmView = ({ candidate, resolvedValues, danger, onConfirm, onCa
16
19
  }
17
20
  if (danger) {
18
21
  if (key.return) {
19
- if (buffer === "EXECUTE") {
22
+ if (isDangerConfirmationInput(buffer)) {
20
23
  onConfirm();
21
24
  }
22
25
  else {
@@ -48,7 +51,10 @@ export const ConfirmView = ({ candidate, resolvedValues, danger, onConfirm, onCa
48
51
  "Risk: ",
49
52
  danger.reason),
50
53
  React.createElement(Box, { marginTop: 1, flexDirection: "column" },
51
- React.createElement(SelectedCommandDisplay, { candidate: candidate, resolvedValues: resolvedValues })),
54
+ React.createElement(SelectedCommandDisplay, { candidate: candidate, resolvedValues: resolvedValues }),
55
+ React.createElement(Text, null,
56
+ "Final command: ",
57
+ React.createElement(Text, { color: "yellow" }, command))),
52
58
  !isDone && (React.createElement(React.Fragment, null,
53
59
  React.createElement(Box, { marginTop: 1 },
54
60
  React.createElement(Text, null,
@@ -62,6 +68,9 @@ export const ConfirmView = ({ candidate, resolvedValues, danger, onConfirm, onCa
62
68
  }
63
69
  return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
64
70
  React.createElement(SelectedCommandDisplay, { candidate: candidate, resolvedValues: resolvedValues }),
71
+ React.createElement(Text, null,
72
+ "Final command: ",
73
+ React.createElement(Text, { color: "yellow" }, command)),
65
74
  !isDone && (React.createElement(Box, { marginTop: 1 },
66
75
  React.createElement(Text, null,
67
76
  "Press ",
@@ -1,80 +1,59 @@
1
- import React, { useState, useMemo } from "react";
1
+ import React, { useState } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import { SelectedCommandDisplay } from "./SelectedCommandDisplay.js";
4
- export const ResolvePlaceholdersView = ({ candidate, placeholders, onResolve, onBack, onCancel, }) => {
5
- const [activeIndex, setActiveIndex] = useState(0);
6
- const [values, setValues] = useState(placeholders.map(() => ""));
7
- const [buffer, setBuffer] = useState("");
8
- const resolvedValuesMap = useMemo(() => {
9
- const map = new Map();
10
- placeholders.forEach((p, i) => {
11
- if (i < activeIndex) {
12
- map.set(p.name, values[i]);
13
- }
14
- });
15
- return map;
16
- }, [placeholders, activeIndex, values]);
17
- const currentBuffer = useMemo(() => ({
18
- name: placeholders[activeIndex].name,
19
- value: buffer,
20
- }), [placeholders, activeIndex, buffer]);
4
+ import { applyPlaceholderResolutionInput, createPlaceholderResolution, getPlaceholderResolutionView, } from "./placeholder-logic.js";
5
+ export const ResolvePlaceholdersView = ({ candidate, onResolve, onBack, onCancel, }) => {
6
+ const [resolutionState, setResolutionState] = useState(() => createPlaceholderResolution(candidate));
7
+ const { currentPlaceholder, currentBuffer, resolvedValues } = getPlaceholderResolutionView(resolutionState);
21
8
  useInput((input, key) => {
22
9
  if (key.ctrl && input === "c") {
23
10
  onCancel();
24
11
  return;
25
12
  }
26
- if (key.escape) {
27
- if (activeIndex === 0) {
28
- onBack();
29
- }
30
- else {
31
- const newValues = [...values];
32
- newValues[activeIndex] = "";
33
- setValues(newValues);
34
- setActiveIndex((prev) => prev - 1);
35
- setBuffer(values[activeIndex - 1]);
36
- }
37
- return;
38
- }
39
- if (key.return) {
40
- const newValues = [...values];
41
- newValues[activeIndex] = buffer;
42
- setValues(newValues);
43
- if (activeIndex === placeholders.length - 1) {
44
- const resultMap = new Map();
45
- placeholders.forEach((p, i) => {
46
- resultMap.set(p.name, newValues[i]);
47
- });
48
- onResolve(resultMap);
49
- }
50
- else {
51
- setActiveIndex((prev) => prev + 1);
52
- setBuffer(values[activeIndex + 1]);
53
- }
13
+ const resolutionInput = toPlaceholderResolutionInput(input, key);
14
+ if (!resolutionInput) {
54
15
  return;
55
16
  }
56
- if (key.backspace || key.delete) {
57
- setBuffer((prev) => prev.slice(0, -1));
58
- return;
59
- }
60
- if (input && !Object.values(key).some(Boolean)) {
61
- setBuffer((prev) => prev + input);
17
+ const transition = applyPlaceholderResolutionInput(resolutionState, resolutionInput);
18
+ switch (transition.type) {
19
+ case "editing":
20
+ setResolutionState(transition.state);
21
+ return;
22
+ case "back-to-selection":
23
+ onBack();
24
+ return;
25
+ case "complete":
26
+ onResolve(transition.resolved);
27
+ return;
62
28
  }
63
- // Handle space and other printable characters that might be in key but not in input if needed
64
- // However ink's useInput input is usually the string.
65
29
  });
66
30
  return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
67
- React.createElement(SelectedCommandDisplay, { candidate: candidate, resolvedValues: resolvedValuesMap, currentBuffer: currentBuffer }),
31
+ React.createElement(SelectedCommandDisplay, { candidate: candidate, resolvedValues: resolvedValues, currentBuffer: currentBuffer }),
68
32
  React.createElement(Text, { color: "green" }, "? Fill command placeholders"),
69
33
  React.createElement(Box, { flexDirection: "column", marginTop: 1 },
70
34
  React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
71
35
  React.createElement(Text, null,
72
- placeholders[activeIndex].name,
36
+ currentPlaceholder.name,
73
37
  ": ",
74
- placeholders[activeIndex].description),
38
+ currentPlaceholder.description),
75
39
  React.createElement(Box, null,
76
40
  React.createElement(Text, { color: "cyan" }, "> "),
77
- React.createElement(Text, null, buffer),
41
+ React.createElement(Text, null, currentBuffer.value),
78
42
  React.createElement(Text, { backgroundColor: "white" }, " ")))),
79
43
  React.createElement(Text, { dimColor: true }, "Press Enter for next value, Esc to go back, Ctrl+C to cancel.")));
80
44
  };
45
+ function toPlaceholderResolutionInput(input, key) {
46
+ if (key.escape) {
47
+ return { type: "escape" };
48
+ }
49
+ if (key.return) {
50
+ return { type: "commit" };
51
+ }
52
+ if (key.backspace || key.delete) {
53
+ return { type: "delete" };
54
+ }
55
+ if (input && !Object.values(key).some(Boolean)) {
56
+ return { type: "append", value: input };
57
+ }
58
+ return undefined;
59
+ }
@@ -5,6 +5,64 @@ export class PlaceholderResolutionError extends Error {
5
5
  this.name = "PlaceholderResolutionError";
6
6
  }
7
7
  }
8
+ export function createPlaceholderResolution(candidate) {
9
+ if (candidate.placeholders.length === 0) {
10
+ throw new PlaceholderResolutionError("cannot resolve command without placeholders");
11
+ }
12
+ return {
13
+ candidate,
14
+ activeIndex: 0,
15
+ values: candidate.placeholders.map(() => ""),
16
+ buffer: "",
17
+ };
18
+ }
19
+ export function getPlaceholderResolutionView(state) {
20
+ const currentPlaceholder = state.candidate.placeholders[state.activeIndex];
21
+ if (!currentPlaceholder) {
22
+ throw new PlaceholderResolutionError("placeholder resolution state is out of bounds");
23
+ }
24
+ const resolvedValues = new Map();
25
+ state.candidate.placeholders.forEach((placeholder, index) => {
26
+ if (index < state.activeIndex) {
27
+ resolvedValues.set(placeholder.name, state.values[index]);
28
+ }
29
+ });
30
+ return {
31
+ currentPlaceholder,
32
+ currentBuffer: {
33
+ name: currentPlaceholder.name,
34
+ value: state.buffer,
35
+ },
36
+ resolvedValues,
37
+ };
38
+ }
39
+ export function applyPlaceholderResolutionInput(state, input) {
40
+ switch (input.type) {
41
+ case "append":
42
+ return {
43
+ type: "editing",
44
+ state: Object.assign(Object.assign({}, state), { buffer: state.buffer + input.value }),
45
+ };
46
+ case "delete":
47
+ return {
48
+ type: "editing",
49
+ state: Object.assign(Object.assign({}, state), { buffer: state.buffer.slice(0, -1) }),
50
+ };
51
+ case "escape":
52
+ return applyEscape(state);
53
+ case "commit":
54
+ return applyCommit(state);
55
+ }
56
+ }
57
+ export function resolveCandidatePlaceholders(candidate, values) {
58
+ const command = replaceCommandPlaceholders(candidate.command, values);
59
+ assertNoUnresolvedPlaceholders(command);
60
+ return {
61
+ candidate,
62
+ values,
63
+ command,
64
+ };
65
+ }
8
66
  export function replaceCommandPlaceholders(command, values) {
9
67
  let resolvedCommand = command;
10
68
  for (const [name, value] of values) {
@@ -17,3 +75,34 @@ export function assertNoUnresolvedPlaceholders(command) {
17
75
  throw new PlaceholderResolutionError("final command contains unresolved placeholders");
18
76
  }
19
77
  }
78
+ function applyEscape(state) {
79
+ if (state.activeIndex === 0) {
80
+ return { type: "back-to-selection" };
81
+ }
82
+ const previousIndex = state.activeIndex - 1;
83
+ const values = [...state.values];
84
+ values[state.activeIndex] = "";
85
+ return {
86
+ type: "editing",
87
+ state: Object.assign(Object.assign({}, state), { activeIndex: previousIndex, values, buffer: values[previousIndex] }),
88
+ };
89
+ }
90
+ function applyCommit(state) {
91
+ const values = [...state.values];
92
+ values[state.activeIndex] = state.buffer;
93
+ if (state.activeIndex === state.candidate.placeholders.length - 1) {
94
+ const resolvedValues = new Map();
95
+ state.candidate.placeholders.forEach((placeholder, index) => {
96
+ resolvedValues.set(placeholder.name, values[index]);
97
+ });
98
+ return {
99
+ type: "complete",
100
+ resolved: resolveCandidatePlaceholders(state.candidate, resolvedValues),
101
+ };
102
+ }
103
+ const nextIndex = state.activeIndex + 1;
104
+ return {
105
+ type: "editing",
106
+ state: Object.assign(Object.assign({}, state), { activeIndex: nextIndex, values, buffer: values[nextIndex] }),
107
+ };
108
+ }
@@ -0,0 +1,19 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { parseAndValidateAiResponse } from "./ai-response.js";
11
+ import { validateUseCommandCandidates } from "./command-tool.js";
12
+ export function generateValidatedCommandCandidates(provider, request) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ const result = yield provider.generateCommands(request);
15
+ const response = parseAndValidateAiResponse(result.rawText);
16
+ validateUseCommandCandidates(response, request.useCommand);
17
+ return response.commands;
18
+ });
19
+ }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@unscientificjszhai/howto",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "description": "Use AI to quickly find commands within the terminal.",
6
+ "license": "MIT",
6
7
  "main": "dist/index.js",
7
8
  "repository": {
8
9
  "type": "git",
@@ -16,7 +17,8 @@
16
17
  "howto": "dist/index.js"
17
18
  },
18
19
  "files": [
19
- "dist"
20
+ "dist",
21
+ "THIRD_PARTY_NOTICES.md"
20
22
  ],
21
23
  "scripts": {
22
24
  "build": "tsc",