v0.2.68 - Replace multi-line skill edit with system editor (/nano/vim/vi) in Step 7 skill picker
This commit is contained in:
@@ -54,11 +54,11 @@ Skills compose by adoption-list order (`10123`) and trigger tags carry runtime e
|
||||
|
||||
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
|
||||
|
||||
## Current Status — v0.2.67
|
||||
## Current Status — v0.2.68
|
||||
|
||||
**Active build — this project is barely working. Experiment at your own risk.**
|
||||
|
||||
> Last release update: v0.2.67 — Add n=none to relay menu to deselect all relays; add blank line before 'Type ok' prompt in admin keypair generation
|
||||
> Last release update: v0.2.68 — Replace multi-line skill edit with system editor (/nano/vim/vi) in Step 7 skill picker
|
||||
|
||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||
- Publishes configured startup events per relay as each relay becomes connected
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define DIDACTYL_VERSION_MAJOR 0
|
||||
#define DIDACTYL_VERSION_MINOR 2
|
||||
#define DIDACTYL_VERSION_PATCH 67
|
||||
#define DIDACTYL_VERSION "v0.2.67"
|
||||
#define DIDACTYL_VERSION_PATCH 68
|
||||
#define DIDACTYL_VERSION "v0.2.68"
|
||||
|
||||
// Agent metadata
|
||||
#define DIDACTYL_NAME "Didactyl"
|
||||
|
||||
+104
-25
@@ -643,6 +643,103 @@ static const skill_descriptor_t DIDACTYL_DEFAULT_SKILLS[] = {
|
||||
#define DIDACTYL_DEFAULT_SKILL_COUNT \
|
||||
((int)(sizeof(DIDACTYL_DEFAULT_SKILLS) / sizeof(DIDACTYL_DEFAULT_SKILLS[0])))
|
||||
|
||||
/* Launch the system editor ($EDITOR, editor, nano, vim, vi) to edit content.
|
||||
* Writes initial_content to a temp file, opens the editor, reads it back.
|
||||
* Returns 0 on success with out_content allocated, -1 on failure. */
|
||||
static int edit_content_with_editor(const char* initial_content, char** out_content) {
|
||||
if (!out_content) return -1;
|
||||
*out_content = NULL;
|
||||
|
||||
char tmp_path[] = "/tmp/didactyl_skill_XXXXXX";
|
||||
int fd = mkstemp(tmp_path);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "%sFailed to create temp file for editing.%s\n", ANSI_RED, ANSI_RESET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (initial_content && initial_content[0] != '\0') {
|
||||
size_t len = strlen(initial_content);
|
||||
ssize_t written = write(fd, initial_content, len);
|
||||
(void)written;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* Detect editor: $EDITOR, then editor (Debian policy), nano, vim, vi. */
|
||||
const char* editor = getenv("EDITOR");
|
||||
if (!editor || editor[0] == '\0') editor = "editor";
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* Child: exec the editor. Try detected editor first, then fallbacks. */
|
||||
execlp(editor, editor, tmp_path, (char*)NULL);
|
||||
execlp("nano", "nano", tmp_path, (char*)NULL);
|
||||
execlp("vim", "vim", tmp_path, (char*)NULL);
|
||||
execlp("vi", "vi", tmp_path, (char*)NULL);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if (pid < 0) {
|
||||
fprintf(stderr, "%sFailed to fork for editor.%s\n", ANSI_RED, ANSI_RESET);
|
||||
unlink(tmp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
fprintf(stderr, "%sEditor exited with error.%s\n", ANSI_RED, ANSI_RESET);
|
||||
unlink(tmp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the file back. */
|
||||
FILE* f = fopen(tmp_path, "r");
|
||||
if (!f) {
|
||||
fprintf(stderr, "%sFailed to read edited file.%s\n", ANSI_RED, ANSI_RESET);
|
||||
unlink(tmp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
if (fsize <= 0) {
|
||||
fprintf(stderr, "%sEdited file is empty.%s\n", ANSI_RED, ANSI_RESET);
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* buf = (char*)malloc((size_t)fsize + 1U);
|
||||
if (!buf) {
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t nread = fread(buf, 1, (size_t)fsize, f);
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
|
||||
if (nread == 0) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[nread] = '\0';
|
||||
|
||||
/* Strip trailing newlines (editor often adds one). */
|
||||
while (nread > 0 && (buf[nread - 1] == '\n' || buf[nread - 1] == '\r')) {
|
||||
buf[nread - 1] = '\0';
|
||||
nread--;
|
||||
}
|
||||
|
||||
*out_content = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prompt the operator to pick which skills to include, and optionally create
|
||||
* custom skills. Shows a toggle-based menu similar to the relay picker. */
|
||||
static int prompt_skill_configuration(didactyl_config_t* cfg, const char* agent_name) {
|
||||
@@ -707,34 +804,16 @@ static int prompt_skill_configuration(didactyl_config_t* cfg, const char* agent_
|
||||
/* Cycle: 0→1 (enabled), 1→2 (edit), 2→0 (disabled) */
|
||||
skill_state[idx]++;
|
||||
if (skill_state[idx] > 2) skill_state[idx] = 0;
|
||||
/* If we entered edit mode, prompt to edit the content. */
|
||||
/* If we entered edit mode, open the system editor. */
|
||||
if (skill_state[idx] == 2) {
|
||||
fprintf(stderr, "\n Editing skill '%s':\n", skill_d_tags[idx] ? skill_d_tags[idx] : "");
|
||||
fprintf(stderr, " Current content:\n%s\n\n", skill_contents[idx] ? skill_contents[idx] : "");
|
||||
fprintf(stderr, " Enter new content (markdown, end with '.' on its own line):\n");
|
||||
char new_content[4096] = {0};
|
||||
size_t content_len = 0;
|
||||
for (;;) {
|
||||
char line[WIZARD_LINE_MAX];
|
||||
if (read_line_prompt(" ", line, sizeof(line)) != 0) {
|
||||
for (int i = 0; i < skill_count; i++) { free(skill_d_tags[i]); free(skill_contents[i]); free(skill_tags[i]); }
|
||||
return -1;
|
||||
}
|
||||
if (strcmp(line, ".") == 0) break;
|
||||
size_t line_len = strlen(line);
|
||||
if (content_len + line_len + 2 > sizeof(new_content)) {
|
||||
fprintf(stderr, "%sContent too long (max %zu bytes).%s\n", ANSI_RED, sizeof(new_content) - 1, ANSI_RESET);
|
||||
break;
|
||||
}
|
||||
if (content_len > 0) new_content[content_len++] = '\n';
|
||||
memcpy(new_content + content_len, line, line_len);
|
||||
content_len += line_len;
|
||||
}
|
||||
if (content_len > 0) {
|
||||
new_content[content_len] = '\0';
|
||||
fprintf(stderr, "\n Opening editor for skill '%s'...\n", skill_d_tags[idx] ? skill_d_tags[idx] : "");
|
||||
char* edited = NULL;
|
||||
if (edit_content_with_editor(skill_contents[idx], &edited) == 0 && edited) {
|
||||
free(skill_contents[idx]);
|
||||
skill_contents[idx] = strdup(new_content);
|
||||
skill_contents[idx] = edited;
|
||||
fprintf(stderr, "%sSkill '%s' updated.%s\n", ANSI_YELLOW, skill_d_tags[idx] ? skill_d_tags[idx] : "", ANSI_RESET);
|
||||
} else {
|
||||
fprintf(stderr, "%sEdit cancelled or failed; keeping original content.%s\n", ANSI_RED, ANSI_RESET);
|
||||
}
|
||||
/* After edit, set back to enabled. */
|
||||
skill_state[idx] = 1;
|
||||
|
||||
Reference in New Issue
Block a user