v0.2.68 - Replace multi-line skill edit with system editor (/nano/vim/vi) in Step 7 skill picker

This commit is contained in:
Didactyl User
2026-08-08 06:55:19 -04:00
parent 0a7f269481
commit cea236ff05
3 changed files with 108 additions and 29 deletions
+2 -2
View File
@@ -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.67Add 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.68Replace 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
View File
@@ -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
View File
@@ -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;