@punkcode/cli 0.1.10 → 0.1.12
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 +134 -11
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -164,6 +164,7 @@ function runClaude(options, callbacks) {
|
|
|
164
164
|
...opts.model && { model: opts.model },
|
|
165
165
|
...opts.allowedTools && { allowedTools: opts.allowedTools },
|
|
166
166
|
...opts.disallowedTools && { disallowedTools: opts.disallowedTools },
|
|
167
|
+
...opts.effort && { effort: opts.effort },
|
|
167
168
|
...opts.maxTurns && { maxTurns: opts.maxTurns },
|
|
168
169
|
systemPrompt: opts.systemPrompt ?? { type: "preset", preset: "claude_code" },
|
|
169
170
|
...options.workingDirectory && { cwd: options.workingDirectory },
|
|
@@ -205,6 +206,8 @@ function runClaude(options, callbacks) {
|
|
|
205
206
|
abort: () => {
|
|
206
207
|
},
|
|
207
208
|
resolvePermission: () => {
|
|
209
|
+
},
|
|
210
|
+
setPermissionMode: async () => {
|
|
208
211
|
}
|
|
209
212
|
};
|
|
210
213
|
}
|
|
@@ -286,6 +289,10 @@ function runClaude(options, callbacks) {
|
|
|
286
289
|
};
|
|
287
290
|
logger.info({ sessionId: sessionInfo.sessionId, commands: sessionInfo.slashCommands.length }, "New chat session info");
|
|
288
291
|
callbacks.onSessionCreated(sessionInfo);
|
|
292
|
+
} else if (sys.subtype === "task_started" && callbacks.onTaskStarted) {
|
|
293
|
+
callbacks.onTaskStarted(sys.task_id, sys.description ?? "", sys.tool_use_id);
|
|
294
|
+
} else if (sys.subtype === "task_notification" && callbacks.onTaskNotification) {
|
|
295
|
+
callbacks.onTaskNotification(sys.task_id, sys.status ?? "completed", sys.summary ?? "", sys.tool_use_id);
|
|
289
296
|
}
|
|
290
297
|
break;
|
|
291
298
|
}
|
|
@@ -316,12 +323,14 @@ function runClaude(options, callbacks) {
|
|
|
316
323
|
},
|
|
317
324
|
resolvePermission: (toolUseId, allow, answers, feedback) => {
|
|
318
325
|
pendingPermissions.get(toolUseId)?.({ allow, answers, feedback });
|
|
319
|
-
}
|
|
326
|
+
},
|
|
327
|
+
setPermissionMode: (mode) => q.setPermissionMode(mode)
|
|
320
328
|
};
|
|
321
329
|
}
|
|
322
330
|
|
|
323
331
|
// src/commands/connect.ts
|
|
324
332
|
import fs3 from "fs";
|
|
333
|
+
import os3 from "os";
|
|
325
334
|
|
|
326
335
|
// src/lib/device-info.ts
|
|
327
336
|
import os from "os";
|
|
@@ -904,6 +913,84 @@ function escapeRegex(str) {
|
|
|
904
913
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
905
914
|
}
|
|
906
915
|
|
|
916
|
+
// src/lib/directory-discovery.ts
|
|
917
|
+
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
918
|
+
async function findProjectDirectory(description, searchRoot, rejections) {
|
|
919
|
+
let prompt2 = `Find up to 3 project directories on this machine that best match: "${description}"
|
|
920
|
+
|
|
921
|
+
Search starting from ${searchRoot}. Rank by relevance. Include a brief reason for each match.`;
|
|
922
|
+
if (rejections?.length) {
|
|
923
|
+
prompt2 += "\n\nThe user rejected these previous suggestions:";
|
|
924
|
+
for (const r of rejections) {
|
|
925
|
+
prompt2 += `
|
|
926
|
+
- ${r.path}${r.feedback ? ` (user said: "${r.feedback}")` : ""}`;
|
|
927
|
+
}
|
|
928
|
+
prompt2 += "\n\nFind different matches based on their feedback.";
|
|
929
|
+
}
|
|
930
|
+
const q = query2({
|
|
931
|
+
prompt: prompt2,
|
|
932
|
+
options: {
|
|
933
|
+
systemPrompt: {
|
|
934
|
+
type: "preset",
|
|
935
|
+
preset: "claude_code",
|
|
936
|
+
append: "IMPORTANT: When searching for directories, always try listing likely parent directories with ls FIRST (e.g. ls ~/github, ls ~/projects). Only use find as a last resort, and always with -maxdepth 3. Never scan the entire home directory."
|
|
937
|
+
},
|
|
938
|
+
permissionMode: "bypassPermissions",
|
|
939
|
+
persistSession: false,
|
|
940
|
+
cwd: searchRoot,
|
|
941
|
+
outputFormat: {
|
|
942
|
+
type: "json_schema",
|
|
943
|
+
schema: {
|
|
944
|
+
type: "object",
|
|
945
|
+
properties: {
|
|
946
|
+
suggestions: {
|
|
947
|
+
type: "array",
|
|
948
|
+
items: {
|
|
949
|
+
type: "object",
|
|
950
|
+
properties: {
|
|
951
|
+
path: { type: "string", description: "Absolute path to the project directory" },
|
|
952
|
+
name: { type: "string", description: "Human-readable project name" },
|
|
953
|
+
reason: { type: "string", description: 'Brief reason why this matches (e.g. "Direct match under github folder")' }
|
|
954
|
+
},
|
|
955
|
+
required: ["path", "name", "reason"]
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
required: ["suggestions"]
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
try {
|
|
965
|
+
for await (const msg of q) {
|
|
966
|
+
if (msg.type === "result") {
|
|
967
|
+
const resultMsg = msg;
|
|
968
|
+
if (resultMsg.subtype === "success") {
|
|
969
|
+
const structured = resultMsg.structured_output;
|
|
970
|
+
if (structured?.suggestions?.length) {
|
|
971
|
+
logger.info({ count: structured.suggestions.length }, "Project directories found (structured)");
|
|
972
|
+
return structured.suggestions;
|
|
973
|
+
}
|
|
974
|
+
const result = resultMsg.result?.trim();
|
|
975
|
+
if (result && result !== "null" && result.startsWith("/")) {
|
|
976
|
+
const path3 = result.split("\n")[0].trim();
|
|
977
|
+
const name = path3.split("/").pop() ?? path3;
|
|
978
|
+
logger.info({ path: path3, name }, "Project directory found (text fallback)");
|
|
979
|
+
return [{ path: path3, name, reason: "Best match" }];
|
|
980
|
+
}
|
|
981
|
+
logger.info("No matching directories found");
|
|
982
|
+
return [];
|
|
983
|
+
}
|
|
984
|
+
logger.warn({ subtype: resultMsg.subtype }, "Directory search query failed");
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
} finally {
|
|
989
|
+
q.close();
|
|
990
|
+
}
|
|
991
|
+
return [];
|
|
992
|
+
}
|
|
993
|
+
|
|
907
994
|
// src/lib/auth.ts
|
|
908
995
|
import fs2 from "fs";
|
|
909
996
|
import path2 from "path";
|
|
@@ -1066,7 +1153,9 @@ async function connect(server, options) {
|
|
|
1066
1153
|
const socket = io(url, {
|
|
1067
1154
|
path: "/socket.io",
|
|
1068
1155
|
transports: ["websocket"],
|
|
1069
|
-
auth:
|
|
1156
|
+
auth: (cb) => {
|
|
1157
|
+
refreshIdToken().then((token) => cb({ token })).catch(() => cb({ token: idToken }));
|
|
1158
|
+
},
|
|
1070
1159
|
reconnection: true,
|
|
1071
1160
|
reconnectionAttempts: Infinity,
|
|
1072
1161
|
reconnectionDelay: 1e3,
|
|
@@ -1086,6 +1175,16 @@ async function connect(server, options) {
|
|
|
1086
1175
|
socket.emit("register", deviceInfo, (response) => {
|
|
1087
1176
|
if (response.success) {
|
|
1088
1177
|
logger.info({ deviceId }, "Registered");
|
|
1178
|
+
} else {
|
|
1179
|
+
logger.warn("Registration failed, retrying in 2s...");
|
|
1180
|
+
setTimeout(() => {
|
|
1181
|
+
if (socket.connected) {
|
|
1182
|
+
socket.emit("register", collectDeviceInfo(deviceId, options.name, options.tag, defaultCwd), (r) => {
|
|
1183
|
+
if (r.success) logger.info({ deviceId }, "Registered (retry)");
|
|
1184
|
+
else logger.error("Registration failed after retry");
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
}, 2e3);
|
|
1089
1188
|
}
|
|
1090
1189
|
});
|
|
1091
1190
|
});
|
|
@@ -1114,14 +1213,23 @@ async function connect(server, options) {
|
|
|
1114
1213
|
handleGetCommands(socket, msg);
|
|
1115
1214
|
}
|
|
1116
1215
|
});
|
|
1216
|
+
socket.on("find-project", (msg) => {
|
|
1217
|
+
if (msg.type === "find-project") {
|
|
1218
|
+
handleFindProject(socket, msg, defaultCwd);
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1117
1221
|
socket.on("cancel", (msg) => {
|
|
1118
1222
|
handleCancel(msg.id, activeSessions);
|
|
1119
1223
|
});
|
|
1120
1224
|
socket.on("permission-response", (msg) => {
|
|
1121
1225
|
const log2 = createChildLogger({ sessionId: msg.requestId });
|
|
1122
|
-
log2.info({ toolUseId: msg.toolUseId, allowed: msg.allow }, msg.allow ? "Permission accepted" : "Permission denied");
|
|
1226
|
+
log2.info({ toolUseId: msg.toolUseId, allowed: msg.allow, permissionMode: msg.permissionMode }, msg.allow ? "Permission accepted" : "Permission denied");
|
|
1123
1227
|
const session = activeSessions.get(msg.requestId);
|
|
1124
1228
|
if (session) {
|
|
1229
|
+
if (msg.permissionMode) {
|
|
1230
|
+
session.setPermissionMode(msg.permissionMode).catch(() => {
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1125
1233
|
session.resolvePermission(msg.toolUseId, msg.allow, msg.answers, msg.feedback);
|
|
1126
1234
|
}
|
|
1127
1235
|
});
|
|
@@ -1130,14 +1238,6 @@ async function connect(server, options) {
|
|
|
1130
1238
|
});
|
|
1131
1239
|
socket.io.on("reconnect_attempt", () => {
|
|
1132
1240
|
logger.info("Reconnecting...");
|
|
1133
|
-
refreshIdToken().then((token) => {
|
|
1134
|
-
socket.auth = { token };
|
|
1135
|
-
}).catch(() => {
|
|
1136
|
-
});
|
|
1137
|
-
});
|
|
1138
|
-
socket.on("reconnect", (attemptNumber) => {
|
|
1139
|
-
logger.info({ attemptNumber }, "Reconnected");
|
|
1140
|
-
socket.emit("register", collectDeviceInfo(deviceId, options.name, options.tag, defaultCwd));
|
|
1141
1241
|
});
|
|
1142
1242
|
socket.on("connect_error", (err) => {
|
|
1143
1243
|
const { reason, ...detail } = formatConnectionError(err);
|
|
@@ -1270,6 +1370,14 @@ function handlePrompt(socket, msg, activeSessions) {
|
|
|
1270
1370
|
reason: req.reason,
|
|
1271
1371
|
blockedPath: req.blockedPath
|
|
1272
1372
|
});
|
|
1373
|
+
},
|
|
1374
|
+
onTaskStarted: (taskId, description, toolUseId) => {
|
|
1375
|
+
log2.info({ taskId, toolUseId, description }, "Task started");
|
|
1376
|
+
send(socket, "response", { type: "task_started", taskId, description, toolUseId, requestId: id });
|
|
1377
|
+
},
|
|
1378
|
+
onTaskNotification: (taskId, status, summary, toolUseId) => {
|
|
1379
|
+
log2.info({ taskId, toolUseId, status, summary }, "Task notification");
|
|
1380
|
+
send(socket, "response", { type: "task_notification", taskId, status, summary, toolUseId, requestId: id });
|
|
1273
1381
|
}
|
|
1274
1382
|
}
|
|
1275
1383
|
);
|
|
@@ -1318,6 +1426,21 @@ async function handleGetCommands(socket, msg) {
|
|
|
1318
1426
|
log2.error({ err }, "Commands error");
|
|
1319
1427
|
}
|
|
1320
1428
|
}
|
|
1429
|
+
async function handleFindProject(socket, msg, _defaultCwd) {
|
|
1430
|
+
const { id, description, rootDirectory, rejections } = msg;
|
|
1431
|
+
const searchRoot = rootDirectory ?? os3.homedir();
|
|
1432
|
+
const log2 = createChildLogger({ requestId: id });
|
|
1433
|
+
log2.info({ description, searchRoot }, "Finding project directory...");
|
|
1434
|
+
try {
|
|
1435
|
+
const suggestions = await findProjectDirectory(description, searchRoot, rejections);
|
|
1436
|
+
send(socket, "response", { type: "project_suggestions", suggestions, requestId: id });
|
|
1437
|
+
log2.info({ count: suggestions.length }, "Project suggestions sent");
|
|
1438
|
+
} catch (err) {
|
|
1439
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1440
|
+
send(socket, "response", { type: "error", message, requestId: id });
|
|
1441
|
+
log2.error({ err }, "Find project error");
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1321
1444
|
async function handleGetContext(socket, msg, defaultCwd) {
|
|
1322
1445
|
const { id, sessionId } = msg;
|
|
1323
1446
|
const workingDirectory = msg.workingDirectory ?? defaultCwd;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@punkcode/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Control Claude Code from your phone",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"vitest": "^4.0.18"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
57
|
-
"@anthropic-ai/sdk": "^0.
|
|
56
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.49",
|
|
57
|
+
"@anthropic-ai/sdk": "^0.78.0",
|
|
58
58
|
"commander": "^14.0.3",
|
|
59
59
|
"execa": "^9.6.1",
|
|
60
60
|
"pino": "^10.3.1",
|