terminal-pilot 0.0.19 → 0.0.21
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/cli.js +1076 -496
- package/dist/cli.js.map +4 -4
- package/dist/commands/close-session.js.map +1 -1
- package/dist/commands/create-session.js.map +1 -1
- package/dist/commands/fill.js.map +1 -1
- package/dist/commands/get-session.js.map +1 -1
- package/dist/commands/index.js +481 -163
- package/dist/commands/index.js.map +4 -4
- package/dist/commands/install.js +448 -130
- package/dist/commands/install.js.map +4 -4
- package/dist/commands/installer.js +154 -60
- package/dist/commands/installer.js.map +4 -4
- package/dist/commands/list-sessions.js.map +1 -1
- package/dist/commands/press-key.js.map +1 -1
- package/dist/commands/read-history.js.map +1 -1
- package/dist/commands/read-screen.js.map +1 -1
- package/dist/commands/resize.js.map +1 -1
- package/dist/commands/runtime.js.map +1 -1
- package/dist/commands/screenshot.js.map +1 -1
- package/dist/commands/send-signal.js.map +1 -1
- package/dist/commands/type.js.map +1 -1
- package/dist/commands/uninstall.js +187 -93
- package/dist/commands/uninstall.js.map +4 -4
- package/dist/commands/wait-for-exit.js.map +1 -1
- package/dist/commands/wait-for.js.map +1 -1
- package/dist/testing/cli-repl.js +1072 -492
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +1082 -502
- package/dist/testing/qa-cli.js.map +4 -4
- package/node_modules/@poe-code/agent-skill-config/README.md +103 -0
- package/node_modules/@poe-code/agent-skill-config/dist/apply.d.ts +25 -0
- package/node_modules/@poe-code/agent-skill-config/dist/apply.js +159 -0
- package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.d.ts +23 -0
- package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.js +341 -0
- package/node_modules/@poe-code/agent-skill-config/dist/configs.d.ts +16 -0
- package/node_modules/@poe-code/agent-skill-config/dist/configs.js +70 -0
- package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.d.ts +1 -0
- package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.js +1 -0
- package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.d.ts +8 -0
- package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.js +159 -0
- package/node_modules/@poe-code/agent-skill-config/dist/index.d.ts +11 -0
- package/node_modules/@poe-code/agent-skill-config/dist/index.js +6 -0
- package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.d.ts +22 -0
- package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.js +87 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates/poe-generate.md +47 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates/terminal-pilot.md +45 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates.d.ts +3 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates.js +63 -0
- package/node_modules/@poe-code/agent-skill-config/dist/types.d.ts +16 -0
- package/node_modules/@poe-code/agent-skill-config/dist/types.js +1 -0
- package/node_modules/@poe-code/agent-skill-config/package.json +24 -0
- package/package.json +4 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveAgentId } from "@poe-code/agent-defs";
|
|
4
|
+
const agentSkillConfigs = {
|
|
5
|
+
"claude-code": {
|
|
6
|
+
globalSkillDir: "~/.claude/skills",
|
|
7
|
+
localSkillDir: ".claude/skills"
|
|
8
|
+
},
|
|
9
|
+
codex: {
|
|
10
|
+
globalSkillDir: "~/.codex/skills",
|
|
11
|
+
localSkillDir: ".codex/skills"
|
|
12
|
+
},
|
|
13
|
+
"gemini-cli": {
|
|
14
|
+
globalSkillDir: "~/.gemini/skills",
|
|
15
|
+
localSkillDir: ".gemini/skills"
|
|
16
|
+
},
|
|
17
|
+
opencode: {
|
|
18
|
+
globalSkillDir: "~/.config/opencode/skills",
|
|
19
|
+
localSkillDir: ".opencode/skills"
|
|
20
|
+
},
|
|
21
|
+
goose: {
|
|
22
|
+
globalSkillDir: "~/.agents/skills",
|
|
23
|
+
localSkillDir: ".agents/skills"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const supportedAgents = Object.freeze(Object.keys(agentSkillConfigs));
|
|
27
|
+
export function resolveAgentSupport(input, registry = agentSkillConfigs) {
|
|
28
|
+
const resolvedId = resolveAgentId(input);
|
|
29
|
+
if (!resolvedId) {
|
|
30
|
+
return { status: "unknown", input };
|
|
31
|
+
}
|
|
32
|
+
const config = registry[resolvedId];
|
|
33
|
+
if (!config) {
|
|
34
|
+
return { status: "unsupported", input, id: resolvedId };
|
|
35
|
+
}
|
|
36
|
+
return { status: "supported", input, id: resolvedId, config: { ...config } };
|
|
37
|
+
}
|
|
38
|
+
export function getAgentConfig(agentId) {
|
|
39
|
+
const support = resolveAgentSupport(agentId);
|
|
40
|
+
return support.status === "supported" ? support.config : undefined;
|
|
41
|
+
}
|
|
42
|
+
function expandHome(targetPath, homeDir = os.homedir()) {
|
|
43
|
+
if (!targetPath?.startsWith("~")) {
|
|
44
|
+
return targetPath;
|
|
45
|
+
}
|
|
46
|
+
if (targetPath === "~") {
|
|
47
|
+
return homeDir;
|
|
48
|
+
}
|
|
49
|
+
// Handle ~./ -> ~/.
|
|
50
|
+
if (targetPath.startsWith("~./")) {
|
|
51
|
+
targetPath = `~/.${targetPath.slice(3)}`;
|
|
52
|
+
}
|
|
53
|
+
let remainder = targetPath.slice(1);
|
|
54
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
55
|
+
remainder = remainder.slice(1);
|
|
56
|
+
}
|
|
57
|
+
else if (remainder.startsWith(".")) {
|
|
58
|
+
remainder = remainder.slice(1);
|
|
59
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
60
|
+
remainder = remainder.slice(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return remainder.length === 0 ? homeDir : path.join(homeDir, remainder);
|
|
64
|
+
}
|
|
65
|
+
export function resolveSkillDir(config, scope, cwd, homeDir) {
|
|
66
|
+
if (scope === "global") {
|
|
67
|
+
return path.resolve(expandHome(config.globalSkillDir, homeDir));
|
|
68
|
+
}
|
|
69
|
+
return path.resolve(cwd, config.localSkillDir);
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type GitDirRunner = (cwd: string) => string | undefined;
|
|
2
|
+
export declare function setGitDirRunnerForTest(runner: GitDirRunner): () => void;
|
|
3
|
+
export declare function appendExcludeBlock(cwd: string, runId: string, entries: string[], opts?: {
|
|
4
|
+
markerPrefix?: string;
|
|
5
|
+
}): string | undefined;
|
|
6
|
+
export declare function removeExcludeBlock(cwd: string, runId: string, opts?: {
|
|
7
|
+
markerPrefix?: string;
|
|
8
|
+
}): void;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const defaultMarkerPrefix = "poe-code-spawn-skills";
|
|
5
|
+
function defaultGitDirRunner(cwd) {
|
|
6
|
+
try {
|
|
7
|
+
return execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
8
|
+
cwd,
|
|
9
|
+
encoding: "utf8",
|
|
10
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11
|
+
}).trim();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
let gitDirRunner = defaultGitDirRunner;
|
|
18
|
+
let tempFileCounter = 0;
|
|
19
|
+
export function setGitDirRunnerForTest(runner) {
|
|
20
|
+
const previous = gitDirRunner;
|
|
21
|
+
gitDirRunner = runner;
|
|
22
|
+
return () => {
|
|
23
|
+
gitDirRunner = previous;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function resolveExcludePath(cwd) {
|
|
27
|
+
const gitDir = gitDirRunner(cwd);
|
|
28
|
+
if (gitDir === undefined || gitDir.length === 0) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return path.join(path.isAbsolute(gitDir) ? gitDir : path.resolve(cwd, gitDir), "info/exclude");
|
|
32
|
+
}
|
|
33
|
+
function markers(runId, markerPrefix) {
|
|
34
|
+
return {
|
|
35
|
+
begin: `# ${markerPrefix}:${runId} begin`,
|
|
36
|
+
end: `# ${markerPrefix}:${runId} end`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function assertSingleLine(value, label) {
|
|
40
|
+
if (value.includes("\n") || value.includes("\r")) {
|
|
41
|
+
throw new Error(`${label} must be a single line`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function readExcludeFile(excludePath) {
|
|
45
|
+
try {
|
|
46
|
+
return fs.readFileSync(excludePath, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isNodeError(error) {
|
|
56
|
+
return error instanceof Error && "code" in error;
|
|
57
|
+
}
|
|
58
|
+
function assertNoSymbolicLink(targetPath) {
|
|
59
|
+
const parsed = path.parse(path.resolve(targetPath));
|
|
60
|
+
let current = parsed.root;
|
|
61
|
+
for (const segment of path.resolve(targetPath).slice(parsed.root.length).split(path.sep)) {
|
|
62
|
+
if (segment.length === 0) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
current = path.join(current, segment);
|
|
66
|
+
try {
|
|
67
|
+
if (fs.lstatSync(current).isSymbolicLink()) {
|
|
68
|
+
throw new Error(`Refusing to update Git exclude path through symbolic link: ${current}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function writeExcludeFile(excludePath, content) {
|
|
80
|
+
assertNoSymbolicLink(excludePath);
|
|
81
|
+
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
|
82
|
+
assertNoSymbolicLink(excludePath);
|
|
83
|
+
const tempPath = `${excludePath}.poe-code-${process.pid}-${tempFileCounter++}.tmp`;
|
|
84
|
+
try {
|
|
85
|
+
fs.writeFileSync(tempPath, content, "utf8");
|
|
86
|
+
fs.renameSync(tempPath, excludePath);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
try {
|
|
90
|
+
fs.rmSync(tempPath, { force: true });
|
|
91
|
+
}
|
|
92
|
+
catch (cleanupError) {
|
|
93
|
+
void cleanupError;
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function removeBlock(content, runId, markerPrefix) {
|
|
99
|
+
const { begin, end } = markers(runId, markerPrefix);
|
|
100
|
+
const lines = content.split("\n");
|
|
101
|
+
const result = [];
|
|
102
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
103
|
+
if (lines[index] === begin) {
|
|
104
|
+
const endIndex = lines.indexOf(end, index + 1);
|
|
105
|
+
if (endIndex !== -1) {
|
|
106
|
+
index = endIndex;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
result.push(lines[index]);
|
|
111
|
+
}
|
|
112
|
+
return result.join("\n");
|
|
113
|
+
}
|
|
114
|
+
function appendBlock(content, runId, entries, markerPrefix) {
|
|
115
|
+
const { begin, end } = markers(runId, markerPrefix);
|
|
116
|
+
const existing = content ?? "";
|
|
117
|
+
const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}\n`;
|
|
118
|
+
return `${prefix}${[begin, ...entries, end, ""].join("\n")}`;
|
|
119
|
+
}
|
|
120
|
+
function nextBlockId(content, runId, markerPrefix) {
|
|
121
|
+
if (content === undefined || !content.includes(markers(runId, markerPrefix).begin)) {
|
|
122
|
+
return runId;
|
|
123
|
+
}
|
|
124
|
+
let suffix = 1;
|
|
125
|
+
while (content.includes(markers(`${runId}:${suffix}`, markerPrefix).begin)) {
|
|
126
|
+
suffix += 1;
|
|
127
|
+
}
|
|
128
|
+
return `${runId}:${suffix}`;
|
|
129
|
+
}
|
|
130
|
+
export function appendExcludeBlock(cwd, runId, entries, opts) {
|
|
131
|
+
assertSingleLine(runId, "runId");
|
|
132
|
+
assertSingleLine(opts?.markerPrefix ?? defaultMarkerPrefix, "markerPrefix");
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
assertSingleLine(entry, "exclude entry");
|
|
135
|
+
}
|
|
136
|
+
const excludePath = resolveExcludePath(cwd);
|
|
137
|
+
if (excludePath === undefined) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
assertNoSymbolicLink(excludePath);
|
|
141
|
+
const content = readExcludeFile(excludePath);
|
|
142
|
+
const markerPrefix = opts?.markerPrefix ?? defaultMarkerPrefix;
|
|
143
|
+
const blockId = nextBlockId(content, runId, markerPrefix);
|
|
144
|
+
writeExcludeFile(excludePath, appendBlock(content, blockId, entries, markerPrefix));
|
|
145
|
+
return blockId;
|
|
146
|
+
}
|
|
147
|
+
export function removeExcludeBlock(cwd, runId, opts) {
|
|
148
|
+
assertSingleLine(runId, "runId");
|
|
149
|
+
assertSingleLine(opts?.markerPrefix ?? defaultMarkerPrefix, "markerPrefix");
|
|
150
|
+
const excludePath = resolveExcludePath(cwd);
|
|
151
|
+
if (excludePath === undefined) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const content = readExcludeFile(excludePath);
|
|
155
|
+
if (content === undefined) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
writeExcludeFile(excludePath, removeBlock(content, runId, opts?.markerPrefix ?? defaultMarkerPrefix));
|
|
159
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { AgentSkillConfig, AgentSupportResult, AgentSupportStatus, SkillScope } from "./configs.js";
|
|
2
|
+
export type { ApplyOptions, SkillFile } from "./types.js";
|
|
3
|
+
export type { SkillResolution, SkillResolutionFailure, SkillSource } from "./resolve-skill-reference.js";
|
|
4
|
+
export type { BridgeEntry, BridgeManifest, BridgeWarning, BridgeWarningKind } from "./bridge-active-skills.js";
|
|
5
|
+
export { supportedAgents, resolveAgentSupport, getAgentConfig, resolveSkillDir } from "./configs.js";
|
|
6
|
+
export { configure, unconfigure, installSkill, UnsupportedAgentError } from "./apply.js";
|
|
7
|
+
export type { InstallSkillOptions, InstallSkillResult } from "./apply.js";
|
|
8
|
+
export { resolveSkillReference } from "./resolve-skill-reference.js";
|
|
9
|
+
export { appendExcludeBlock, removeExcludeBlock } from "./git-exclude.js";
|
|
10
|
+
export { setGitDirRunnerForTest } from "./git-exclude.js";
|
|
11
|
+
export { bridgeActiveSkills, cleanupBridgedSkills } from "./bridge-active-skills.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { supportedAgents, resolveAgentSupport, getAgentConfig, resolveSkillDir } from "./configs.js";
|
|
2
|
+
export { configure, unconfigure, installSkill, UnsupportedAgentError } from "./apply.js";
|
|
3
|
+
export { resolveSkillReference } from "./resolve-skill-reference.js";
|
|
4
|
+
export { appendExcludeBlock, removeExcludeBlock } from "./git-exclude.js";
|
|
5
|
+
export { setGitDirRunnerForTest } from "./git-exclude.js";
|
|
6
|
+
export { bridgeActiveSkills, cleanupBridgedSkills } from "./bridge-active-skills.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface SkillSource {
|
|
2
|
+
kind: "resolved";
|
|
3
|
+
ref: string;
|
|
4
|
+
name: string;
|
|
5
|
+
sourceAgentId?: string;
|
|
6
|
+
sourcePath: string;
|
|
7
|
+
scope: "project" | "user";
|
|
8
|
+
}
|
|
9
|
+
export type SkillResolutionFailure = {
|
|
10
|
+
kind: "malformed";
|
|
11
|
+
ref: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "unknown-agent";
|
|
14
|
+
ref: string;
|
|
15
|
+
agentInput: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "not-found";
|
|
18
|
+
ref: string;
|
|
19
|
+
searchedPaths: string[];
|
|
20
|
+
};
|
|
21
|
+
export type SkillResolution = SkillSource | SkillResolutionFailure;
|
|
22
|
+
export declare function resolveSkillReference(ref: string, cwd: string, homeDir: string): SkillResolution;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getAgentConfig, resolveAgentSupport, resolveSkillDir } from "./configs.js";
|
|
4
|
+
function isMalformedSegment(segment) {
|
|
5
|
+
return (segment.length === 0 ||
|
|
6
|
+
segment !== segment.trim() ||
|
|
7
|
+
segment === "." ||
|
|
8
|
+
segment === ".." ||
|
|
9
|
+
segment.includes("\n") ||
|
|
10
|
+
segment.includes("\r"));
|
|
11
|
+
}
|
|
12
|
+
function isDirectory(targetPath) {
|
|
13
|
+
try {
|
|
14
|
+
return statSync(targetPath).isDirectory();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function findSkill(ref, name, tiers, sourceAgentId) {
|
|
21
|
+
for (const tier of tiers) {
|
|
22
|
+
if (isDirectory(tier.sourcePath)) {
|
|
23
|
+
return {
|
|
24
|
+
kind: "resolved",
|
|
25
|
+
ref,
|
|
26
|
+
name,
|
|
27
|
+
...(sourceAgentId ? { sourceAgentId } : {}),
|
|
28
|
+
sourcePath: tier.sourcePath,
|
|
29
|
+
scope: tier.scope
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
kind: "not-found",
|
|
35
|
+
ref,
|
|
36
|
+
searchedPaths: tiers.map((tier) => tier.sourcePath)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function resolveSkillReference(ref, cwd, homeDir) {
|
|
40
|
+
const slashIndex = ref.indexOf("/");
|
|
41
|
+
const hasPrefix = slashIndex !== -1;
|
|
42
|
+
if (ref.length === 0 ||
|
|
43
|
+
ref !== ref.trim() ||
|
|
44
|
+
(hasPrefix && ref.indexOf("/", slashIndex + 1) !== -1)) {
|
|
45
|
+
return { kind: "malformed", ref };
|
|
46
|
+
}
|
|
47
|
+
if (!hasPrefix) {
|
|
48
|
+
if (isMalformedSegment(ref)) {
|
|
49
|
+
return { kind: "malformed", ref };
|
|
50
|
+
}
|
|
51
|
+
const tiers = [
|
|
52
|
+
{
|
|
53
|
+
scope: "project",
|
|
54
|
+
sourcePath: path.resolve(cwd, ".poe-code/skills", ref)
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
scope: "user",
|
|
58
|
+
sourcePath: path.resolve(homeDir, ".poe-code/skills", ref)
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
return findSkill(ref, ref, tiers);
|
|
62
|
+
}
|
|
63
|
+
const agentInput = ref.slice(0, slashIndex);
|
|
64
|
+
const name = ref.slice(slashIndex + 1);
|
|
65
|
+
if (isMalformedSegment(agentInput) || isMalformedSegment(name)) {
|
|
66
|
+
return { kind: "malformed", ref };
|
|
67
|
+
}
|
|
68
|
+
const support = resolveAgentSupport(agentInput);
|
|
69
|
+
if (support.status !== "supported" || !support.id) {
|
|
70
|
+
return { kind: "unknown-agent", ref, agentInput };
|
|
71
|
+
}
|
|
72
|
+
const config = getAgentConfig(support.id);
|
|
73
|
+
if (!config) {
|
|
74
|
+
return { kind: "unknown-agent", ref, agentInput };
|
|
75
|
+
}
|
|
76
|
+
const tiers = [
|
|
77
|
+
{
|
|
78
|
+
scope: "project",
|
|
79
|
+
sourcePath: path.resolve(resolveSkillDir(config, "local", cwd), name)
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
scope: "user",
|
|
83
|
+
sourcePath: path.resolve(resolveSkillDir(config, "global", cwd, homeDir), name)
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
return findSkill(ref, name, tiers, support.id);
|
|
87
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: poe-generate
|
|
3
|
+
description: 'Poe code generation skill'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# poe-code generate
|
|
7
|
+
|
|
8
|
+
Use `poe-code generate` to create text, images, audio, or video via the Poe API.
|
|
9
|
+
|
|
10
|
+
## Text generation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
poe-code generate "Write a short function that parses a JSON string safely."
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Specify the model/bot:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# CLI option
|
|
20
|
+
poe-code generate --model "gpt-4.1" "Summarize this codebase change."
|
|
21
|
+
|
|
22
|
+
# Some agent runtimes call the model selector `--bot`
|
|
23
|
+
poe-code generate --bot "gpt-4.1" "Summarize this codebase change."
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Media generation
|
|
27
|
+
|
|
28
|
+
The CLI supports media generation as subcommands:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
poe-code generate image "A 3D render of a rubber duck wearing sunglasses" --model "gpt-image-1" -o duck.png
|
|
32
|
+
poe-code generate video "A cinematic timelapse of a city at night" --model "veo" -o city.mp4
|
|
33
|
+
poe-code generate audio "A calm 10 second lo-fi beat" --model "audio-model" -o beat.wav
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Some agent runtimes expose the same media types as flags. If available, these are equivalent:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
poe-code generate --image "A 3D render of a rubber duck wearing sunglasses" --bot "gpt-image-1" -o duck.png
|
|
40
|
+
poe-code generate --video "A cinematic timelapse of a city at night" --bot "veo" -o city.mp4
|
|
41
|
+
poe-code generate --audio "A calm 10 second lo-fi beat" --bot "audio-model" -o beat.wav
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Tips
|
|
45
|
+
|
|
46
|
+
- Use `--param key=value` to pass provider/model parameters (repeatable).
|
|
47
|
+
- Use `--output <path>` (or `-o`) for media outputs.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: terminal-pilot
|
|
3
|
+
description: 'Terminal automation skill using the terminal-pilot CLI'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Terminal Pilot
|
|
7
|
+
|
|
8
|
+
Use the `terminal-pilot` CLI when you need to automate or inspect interactive
|
|
9
|
+
CLI applications through a real PTY session.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
- `terminal-pilot create-session` - start a PTY-backed command
|
|
14
|
+
- `terminal-pilot fill` - paste text into a session
|
|
15
|
+
- `terminal-pilot type` - type character-by-character for TUIs and readline
|
|
16
|
+
- `terminal-pilot press-key` - send named keys such as `Enter` or `ArrowDown`
|
|
17
|
+
- `terminal-pilot wait-for` - wait for terminal output to match a pattern
|
|
18
|
+
- `terminal-pilot wait-for-exit` - block until a session exits
|
|
19
|
+
- `terminal-pilot read-screen` - inspect the current visible terminal screen
|
|
20
|
+
- `terminal-pilot read-history` - read scrollback output
|
|
21
|
+
- `terminal-pilot list-sessions` - list active sessions
|
|
22
|
+
- `terminal-pilot close-session` - close a session and return its exit code
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
terminal-pilot --help
|
|
28
|
+
terminal-pilot create-session --help
|
|
29
|
+
terminal-pilot read-screen --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use JSON output when another tool or script needs to read the result:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
terminal-pilot list-sessions --output json
|
|
36
|
+
terminal-pilot read-screen --session s1 --output json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Tips
|
|
40
|
+
|
|
41
|
+
- Use `fill` for pasted text and multi-line input.
|
|
42
|
+
- Use `type` when the app reacts to individual keystrokes.
|
|
43
|
+
- Use `press-key` for Enter, Tab, arrow keys, Escape, and control-key chords.
|
|
44
|
+
- Use `wait-for --literal` for exact string matching.
|
|
45
|
+
- Default terminal size is 120x40.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const TEMPLATE_IDS = ["poe-generate.md", "terminal-pilot.md"];
|
|
5
|
+
const cache = new Map();
|
|
6
|
+
async function pathExists(target) {
|
|
7
|
+
try {
|
|
8
|
+
await stat(target);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function findPackageRoot(entryFilePath) {
|
|
19
|
+
let current = path.dirname(entryFilePath);
|
|
20
|
+
while (true) {
|
|
21
|
+
if (await pathExists(path.join(current, "package.json"))) {
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(current);
|
|
25
|
+
if (parent === current) {
|
|
26
|
+
throw new Error("Unable to locate package root for agent-skill-config templates.");
|
|
27
|
+
}
|
|
28
|
+
current = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function resolveTemplatePath(templateId) {
|
|
32
|
+
const packageRoot = await findPackageRoot(fileURLToPath(import.meta.url));
|
|
33
|
+
const candidates = [
|
|
34
|
+
path.join(packageRoot, "src", "templates", templateId),
|
|
35
|
+
path.join(packageRoot, "dist", "templates", templateId),
|
|
36
|
+
path.join(packageRoot, "dist", "templates", "skill", templateId)
|
|
37
|
+
];
|
|
38
|
+
for (const candidate of candidates) {
|
|
39
|
+
if (await pathExists(candidate)) {
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
44
|
+
}
|
|
45
|
+
function isKnownTemplate(templateId) {
|
|
46
|
+
return TEMPLATE_IDS.includes(templateId);
|
|
47
|
+
}
|
|
48
|
+
export async function loadTemplate(templateId) {
|
|
49
|
+
if (!isKnownTemplate(templateId)) {
|
|
50
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
51
|
+
}
|
|
52
|
+
const cached = cache.get(templateId);
|
|
53
|
+
if (cached !== undefined) {
|
|
54
|
+
return cached;
|
|
55
|
+
}
|
|
56
|
+
const resolved = await resolveTemplatePath(templateId);
|
|
57
|
+
const content = await readFile(resolved, "utf8");
|
|
58
|
+
cache.set(templateId, content);
|
|
59
|
+
return content;
|
|
60
|
+
}
|
|
61
|
+
export function createTemplateLoader() {
|
|
62
|
+
return loadTemplate;
|
|
63
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileSystem, MutationObservers } from "@poe-code/config-mutations";
|
|
2
|
+
import type { SkillScope } from "./configs.js";
|
|
3
|
+
export interface ApplyOptions {
|
|
4
|
+
fs: FileSystem;
|
|
5
|
+
homeDir: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
scope?: SkillScope;
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
observers?: MutationObservers;
|
|
10
|
+
}
|
|
11
|
+
export interface SkillFile {
|
|
12
|
+
/** Skill folder name */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Content to write to SKILL.md */
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@poe-code/agent-skill-config",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "node ../../scripts/guard-package-dist.mjs && tsc && node scripts/copy-templates.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@poe-code/agent-defs": "*",
|
|
22
|
+
"@poe-code/config-mutations": "*"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminal-pilot",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"description": "Playwright-like SDK and CLI for automating interactive CLI apps through a real PTY",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "rm -rf dist && tsc --emitDeclarationOnly && node scripts/build.mjs",
|
|
27
|
-
"prepack": "node ../../scripts/set-bin-executable.mjs",
|
|
27
|
+
"prepack": "node ../../scripts/set-bin-executable.mjs && node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/agent-skill-config",
|
|
28
|
+
"postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/agent-skill-config",
|
|
28
29
|
"test": "cd ../.. && vitest run $(rg --files packages/terminal-pilot/src -g '*.test.ts' | sort | tr '\n' ' ')",
|
|
29
30
|
"test:unit": "cd ../.. && vitest run $(rg --files packages/terminal-pilot/src -g '*.test.ts' | sort | tr '\n' ' ')",
|
|
30
31
|
"qa:cli": "cd ../.. && tsx packages/terminal-pilot/src/testing/qa-cli.ts",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"jsonc-parser": "^3.3.1",
|
|
61
62
|
"node-pty": "^1.1.0",
|
|
62
63
|
"smol-toml": "^1.6.0",
|
|
64
|
+
"toolcraft-design": "^0.0.2",
|
|
63
65
|
"yaml": "^2.8.2"
|
|
64
66
|
},
|
|
65
67
|
"bundleDependencies": [
|