From 475b54eb6ad7ff6d0eeff945144e843854b97d1b Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 2 Sep 2026 22:16:30 +0000 Subject: [PATCH] package: align new project manifests --- README.md | 2 ++ docs/CURRENT_STATUS.md | 1 + src/claro.c | 2 +- tools/validate_package_security.py | 14 ++++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 418d255..df60d8b 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,8 @@ claro.lock packages/ ``` +Starter projects created with `claro new MyProject` use the same `manifest-version: 1` and `lock-version: 1` headers as `claro package init`, so the first project files match the package maintenance tools. + Package names are checked so unsafe names such as `../bad` are rejected when adding packages. Names must also be 64 characters or fewer, which keeps generated package paths predictable. If an unsafe name is already present in `claro.project`, `claro package doctor`, `claro package lock`, and lockfile refreshes during `claro package remove` flag it instead of treating it as safe lockfile data. diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md index 10a0b89..de0e704 100644 --- a/docs/CURRENT_STATUS.md +++ b/docs/CURRENT_STATUS.md @@ -96,6 +96,7 @@ Ready now: - `claro package init` - `claro package add/list/remove/doctor/lock` - local project files such as `claro.project`, `claro.lock`, and `packages/` +- starter projects from `claro new` now use the same manifest-version and lock-version headers as `claro package init` - package-name safety checks when adding packages, including the 64-character package-name limit, when `claro package doctor` audits an existing `claro.project`, when `claro package lock` writes lockfile data, and when `claro package remove` refreshes the lockfile after an edit Still needed: diff --git a/src/claro.c b/src/claro.c index 7d238c4..784643e 100644 --- a/src/claro.c +++ b/src/claro.c @@ -618,7 +618,7 @@ static int run_tests(void){ int fails=0; printf("%s: %d failure(s)\n",fails?"FAIL":"PASS",fails); return fails?1:0; } static int write_text_file_simple(const char *path,const char *text){ FILE *f=fopen(path,"wb"); if(!f) return 0; fputs(text,f); fclose(f); return 1; } -static int create_new_project(const char *name){ char path[512]; char text[1024]; if(!name||!*name){ fprintf(stderr,"Project needs a name. Try: claro new MyProject\n"); return 1; } if(!make_folder(name)){ fprintf(stderr,"Could not create project folder: %s\n",name); return 1; } snprintf(path,sizeof(path),"%s/main.claro",name); write_text_file_simple(path,"SAY \"Welcome to Claro!\"\n\nSET name TO \"Learner\"\nSAY \"Hello \" + name\n\nTEACH greet person\n SAY \"Nice to meet you, \" + person\nEND\n\nDO greet \"Friend\"\n"); snprintf(path,sizeof(path),"%s/packages",name); make_folder(path); snprintf(path,sizeof(path),"%s/claro.project",name); snprintf(text,sizeof(text),"name: %s\nmain: main.claro\nversion: v1.18.26\npackages:\n",name); write_text_file_simple(path,text); snprintf(path,sizeof(path),"%s/claro.lock",name); write_text_file_simple(path,"# Claro package lock\nversion: v1.18.26\n"); snprintf(path,sizeof(path),"%s/README.md",name); write_text_file_simple(path,"# My Claro Project\n\nBuild Claro, then run this project with:\n\n```bash\nclaro run\n```\n\nUse packages with:\n\n```bash\nclaro package add text\nclaro package list\nclaro package doctor\n```\n"); printf("Created Claro project: %s\n",name); printf("Next steps:\n cd %s\n claro run\n",name); return 0; } +static int create_new_project(const char *name){ char path[512]; char text[1024]; if(!name||!*name){ fprintf(stderr,"Project needs a name. Try: claro new MyProject\n"); return 1; } if(!make_folder(name)){ fprintf(stderr,"Could not create project folder: %s\n",name); return 1; } snprintf(path,sizeof(path),"%s/main.claro",name); write_text_file_simple(path,"SAY \"Welcome to Claro!\"\n\nSET name TO \"Learner\"\nSAY \"Hello \" + name\n\nTEACH greet person\n SAY \"Nice to meet you, \" + person\nEND\n\nDO greet \"Friend\"\n"); snprintf(path,sizeof(path),"%s/packages",name); make_folder(path); snprintf(path,sizeof(path),"%s/claro.project",name); snprintf(text,sizeof(text),"manifest-version: 1\nname: %s\nmain: main.claro\nversion: v1.18.26\npackages:\n",name); write_text_file_simple(path,text); snprintf(path,sizeof(path),"%s/claro.lock",name); write_text_file_simple(path,"# Claro package lock\nlock-version: 1\nversion: v1.18.26\n"); snprintf(path,sizeof(path),"%s/README.md",name); write_text_file_simple(path,"# My Claro Project\n\nBuild Claro, then run this project with:\n\n```bash\nclaro run\n```\n\nUse packages with:\n\n```bash\nclaro package add text\nclaro package list\nclaro package doctor\n```\n"); printf("Created Claro project: %s\n",name); printf("Next steps:\n cd %s\n claro run\n",name); return 0; } static int run_repl(void){ Runtime rt; char line[4096]; Program p; rt_init(&rt); memset(&p,0,sizeof(p)); p.path=xstrdup(""); p.count=1; p.cap=1; p.lines=(char**)xmalloc(sizeof(char*)); printf("%s\n",CLARO_VERSION); printf("Type HELP for help, or EXIT to leave.\n"); while(1){ int pc=0; printf("> "); fflush(stdout); if(!fgets(line,sizeof(line),stdin)) break; { size_t n=strlen(line); while(n&&(line[n-1]=='\n'||line[n-1]=='\r')) line[--n]=0; } { char *t=trim_inplace(line); if(ci_eq(t,"EXIT")||ci_eq(t,"QUIT")) break; if(ci_eq(t,"HELP")){ printf("Try: SAY \"Hello\"\n"); printf("Try: SET name TO \"Jon\"\n"); printf("Try: SAY name\n"); continue; } if(!*t) continue; p.lines[0]=t; exec_line(&rt,&p,&pc,t); if(rt.error){ fprintf(stderr,"%s:%d: %s\n",rt.err_file?rt.err_file:"",rt.err_line,rt.err_msg?rt.err_msg:"error"); rt_clear_error(&rt); } } } return 0; } static int file_exists_simple(const char *path); diff --git a/tools/validate_package_security.py b/tools/validate_package_security.py index 5bda8f2..45425a6 100644 --- a/tools/validate_package_security.py +++ b/tools/validate_package_security.py @@ -32,6 +32,20 @@ def main(): with tempfile.TemporaryDirectory() as td: work = Path(td) + + rc, out = run([str(EXE), "new", "StarterApp"], work) + if rc != 0: + fail(out) + starter = work / "StarterApp" + starter_project = read(starter / "claro.project") + for phrase in ["manifest-version: 1", "name: StarterApp", "main: main.claro", "version: v1.18.26", "packages:"]: + if phrase not in starter_project: + fail(f"claro new project file missing {phrase!r}:\n{starter_project}") + starter_lock = read(starter / "claro.lock") + for phrase in ["lock-version: 1", "version: v1.18.26"]: + if phrase not in starter_lock: + fail(f"claro new lockfile missing {phrase!r}:\n{starter_lock}") + (work / "main.claro").write_text('SAY "Package security demo"\n', encoding="utf-8") rc, out = run([str(EXE), "package", "init"], work)