@zythegit/agentforge 0.2.3-rc.2 → 0.2.3-rc.3
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/README.md +4 -0
- package/dist/aforge.js +4 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -56,10 +56,14 @@ aforge status # SoT 概览:scope、各 target 落点与技能调
|
|
|
56
56
|
aforge doctor # 体检:配置合法性、投影一致性、环境问题
|
|
57
57
|
aforge sync --dry-run # 只看会写哪些文件,不落盘
|
|
58
58
|
aforge learn # 记一条 learning(不投影,promote 后才进规则)
|
|
59
|
+
aforge learn --file - # 从 stdin 读正文(管道 / agent 调用;非交互终端必须走这条)
|
|
60
|
+
aforge learn --file notes.md # 从文件读正文
|
|
59
61
|
aforge skill add <name> # 装技能进 SoT 并登记,sync 后投影到四家
|
|
60
62
|
aforge mcp add # 登记 MCP 服务器,sync 时翻译成各 Agent 的原生配置
|
|
61
63
|
```
|
|
62
64
|
|
|
65
|
+
让 agent 自己沉淀经验:把 `learning.auto_capture` 设为 `prompt`(投影正文里多一段协议)或 `hook`(codex 侧会话钩子注入)。没有钩子落点的三家可以手工挂载同一份协议,写法见 [learning](docs/learning.md#手工挂载把协议塞进没有落点的三家)。
|
|
66
|
+
|
|
63
67
|
任何子命令都可加 `--json` 拿机器可读输出(路径一律绝对路径)。完整 14 个命令、参数与退出码见 [命令速查](docs/commands.md)。
|
|
64
68
|
|
|
65
69
|
## 工作原理
|
package/dist/aforge.js
CHANGED
|
@@ -448,6 +448,9 @@ aforge learn --file -
|
|
|
448
448
|
Rules for what you write:
|
|
449
449
|
|
|
450
450
|
- Structured summary only: one convention per entry, phrased as an instruction.
|
|
451
|
+
- One entry per invocation: run the command again for a second convention.
|
|
452
|
+
- Entries are project-scoped by default. Add \`--scope user\` only when the
|
|
453
|
+
convention holds across all of this user's projects.
|
|
451
454
|
- Never paste raw session transcripts, tool output, secrets or tokens.
|
|
452
455
|
- Recording does not activate the rule. Projection stays a human step
|
|
453
456
|
(\`aforge sync\`), so do not run it yourself.
|
|
@@ -527,7 +530,7 @@ Before finishing: run {{#each ai.verification}}{{#if @first}}{{this}}{{else if @
|
|
|
527
530
|
${n.map(i=>` - ${i}`).join(`
|
|
528
531
|
`)}`,{hint:"bundle \u5728\u4F20\u8F93\u4E2D\u88AB\u6539\u52A8\u6216\u635F\u574F\uFF1B\u91CD\u65B0\u5BFC\u51FA\u4E00\u4EFD\uFF08\u54C8\u5E0C\u6309 LF \u89C4\u8303\u5316\u8BA1\u7B97\uFF0C\u6362\u884C\u5DEE\u5F02\u4E0D\u4F1A\u8BEF\u62A5\uFF09",details:{problems:n}});return o}a(kB,"verifyFiles");async function wB(e,t){let r=`${t}${tk}`;if(!await e.exists(r))return r;for(let o=2;o<=rk;o+=1){let n=`${t}${tk}-${o}`;if(!await e.exists(n))return n}throw new be(`\u91CD\u547D\u540D\u843D\u70B9\u5168\u90E8\u88AB\u5360\u7528: ${r}\uFF08\u5DF2\u5C1D\u8BD5 ${rk} \u4E2A\u540E\u7F00\uFF09`,{hint:"\u624B\u5DE5\u6E05\u7406\u76EE\u6807\u76EE\u5F55\u91CC\u7684 *.imported* \u6B8B\u7559\u540E\u91CD\u8BD5",details:{target:t,suffix:tk,attempts:rk}})}a(wB,"resolveRenameTarget");async function xB(e,t,r,o){let n=En.join(t,...r.rel.split("/"));if(await e.exists(n)){if(o==="skip")return{path:r.rel,target:n,action:"skipped"};if(o==="rename"){let i=await wB(e,n);return await ce(e,En.dirname(i)),await ee(e,i,r.content),{path:r.rel,target:i,action:"renamed"}}}return await ce(e,En.dirname(n)),await ee(e,n,r.content),{path:r.rel,target:n,action:"written"}}a(xB,"writeOne");async function IB(e,t,r){let o=new Set,n=[];for(let i of r){let s=i.rel.split("/"),c=t;for(let l of s)c=En.join(c,l),!o.has(c)&&(o.add(c),await dp(e,c)&&n.push(`${c}\uFF08\u5BFC\u5165 ${i.rel} \u65F6\u4F1A\u7A7F\u900F\u5230\u94FE\u63A5\u76EE\u6807\uFF09`))}if(n.length>0)throw new k(`\u76EE\u6807 SoT \u7684\u8DEF\u5F84\u4E0A\u5B58\u5728 symlink\uFF0C\u62D2\u7EDD\u5199\u5165:
|
|
529
532
|
${n.map(i=>` - ${i}`).join(`
|
|
530
|
-
`)}`,{hint:"\u628A\u8FD9\u4E9B symlink \u6362\u6210\u771F\u5B9E\u76EE\u5F55\uFF08\u6216\u6539\u7528 --scope \u843D\u5230\u53E6\u4E00\u5C42\uFF09\u540E\u91CD\u8BD5\uFF1Bbundle \u4E0D\u8DDF\u968F symlink \u5199\u76D8",details:{sotRoot:t,problems:n}})}a(IB,"assertNoSymlinkOnWritePath");function EB(e,t){return t==="project"?le(e.cwd,e.os):te(V(e.host),e.os)}a(EB,"resolveTargetSoT");async function oT(e,t){let r=En.resolve(e.cwd,t.from),o=En.join(r,up),n=t.onConflict??"skip",i=t.scope??V(e.host).agfScope??"project",s=EB(e,i),c=await $B(e.host,r),l=await kB(e.host,o,c),u=new Set(l.map(f=>f.rel)),p=(await Da(e.host,e.os,o)).files.filter(f=>!u.has(f));await ce(e.host,s);let d=await Ue(e.host,s,e.os,async()=>{await IB(e.host,s,l);let f=[];for(let m of l)f.push(await xB(e.host,s,m,n));return f});return{scope:i,sotRoot:s,bundleDir:r,manifest:c,onConflict:n,entries:d,unlisted:p}}a(oT,"importBundle");H();var Pn="0.2.3-rc.
|
|
533
|
+
`)}`,{hint:"\u628A\u8FD9\u4E9B symlink \u6362\u6210\u771F\u5B9E\u76EE\u5F55\uFF08\u6216\u6539\u7528 --scope \u843D\u5230\u53E6\u4E00\u5C42\uFF09\u540E\u91CD\u8BD5\uFF1Bbundle \u4E0D\u8DDF\u968F symlink \u5199\u76D8",details:{sotRoot:t,problems:n}})}a(IB,"assertNoSymlinkOnWritePath");function EB(e,t){return t==="project"?le(e.cwd,e.os):te(V(e.host),e.os)}a(EB,"resolveTargetSoT");async function oT(e,t){let r=En.resolve(e.cwd,t.from),o=En.join(r,up),n=t.onConflict??"skip",i=t.scope??V(e.host).agfScope??"project",s=EB(e,i),c=await $B(e.host,r),l=await kB(e.host,o,c),u=new Set(l.map(f=>f.rel)),p=(await Da(e.host,e.os,o)).files.filter(f=>!u.has(f));await ce(e.host,s);let d=await Ue(e.host,s,e.os,async()=>{await IB(e.host,s,l);let f=[];for(let m of l)f.push(await xB(e.host,s,m,n));return f});return{scope:i,sotRoot:s,bundleDir:r,manifest:c,onConflict:n,entries:d,unlisted:p}}a(oT,"importBundle");H();var Pn="0.2.3-rc.3";async function CB(e,t){return nT({host:e.host,cwd:e.cwd,os:e.os,agentforgeVersion:Pn},{out:t.out,scope:t.scope,redact:t.redact,keepDetected:t.keepDetected})}a(CB,"runBundleExport");async function OB(e,t){return oT({host:e.host,cwd:e.cwd,os:e.os},{from:t.from,scope:t.scope,onConflict:t.onConflict})}a(OB,"runBundleImport");function AB(e){if(e!==void 0){if(!nk.includes(e))throw new k(`\u975E\u6CD5 --on-conflict: ${e}`,{hint:`\u6709\u6548\u503C: ${nk.join(", ")}`,details:{onConflict:e}});return e}}a(AB,"parseConflictPolicy");var iT=20;function pp(e,t,r){for(let n of t.slice(0,iT))e.push(`${r}${n}`);let o=t.length-iT;o>0&&e.push(`${r}... and ${o} more (use --json for the full list)`)}a(pp,"pushCapped");var or=10;function TB(e,t=F()){let r=e.manifest,o=[`${t.green("bundle exported")}: ${t.path(e.outDir)}`,t.kv("scope",`${e.scope} (${t.path(e.sotRoot)})`,or),t.kv("manifest",t.path(e.manifestFile),or),t.kv("content",t.path(e.contentDir),or),t.kv("files",`${r.files.length} file(s) carried`,or)];if(pp(o,r.files.map(n=>t.dim(`- ${n.path}${n.transformed?" [transformed]":""}`))," "),r.excluded.length>0&&(o.push(t.kv("excluded",`${r.excluded.length} entry(ies) left behind`,or)),pp(o,r.excluded.map(n=>t.dim(`- ${n.path} (${n.reason})`))," ")),r.redacted.length>0&&(o.push(t.kv("redacted",t.yellow(`${r.redacted.length} credential value(s) replaced by a placeholder`),or)),pp(o,r.redacted.map(n=>`- ${n}`)," ")),r.warnings.length>0){o.push(` ${t.yellow("warnings :")}`);for(let n of r.warnings)o.push(` - ${t.yellow(n)}`)}return o.push("",t.next("move this directory to the target machine, then run"),` ${t.code(`aforge bundle import --from ${e.outDir}`)}`),o.join(`
|
|
531
534
|
`)}a(TB,"renderExport");var sT="=".repeat(72);function RB(e,t,r){let o=t.manifest.redacted;if(o.length!==0){e.push("",r.red(sT),`${r.badge("warn")} ${r.yellow(`${o.length} credential value(s) are placeholders.`)}`),e.push(r.yellow(" The MCP server(s) below will fail auth until you set them again:"));for(let n of o)e.push(` - ${r.bold(n)}`);e.push(` Edit ${r.path(PB.join(t.sotRoot,"profile.yaml"))}, then run ${r.code("aforge sync")}.`,r.red(sT))}}a(RB,"pushCredentialAlert");function jB(e,t=F()){let r={written:e.entries.filter(n=>n.action==="written").length,skipped:e.entries.filter(n=>n.action==="skipped").length,renamed:e.entries.filter(n=>n.action==="renamed").length},o=[`${t.green("bundle imported")}: ${t.path(e.bundleDir)}`,t.kv("scope",`${e.scope} (${t.path(e.sotRoot)})`,or),t.kv("policy",e.onConflict,or),t.kv("written",String(r.written),or),t.kv("skipped",String(r.skipped),or),t.kv("renamed",String(r.renamed),or)];if(pp(o,e.entries.map(n=>t.dim(`- ${n.path} [${n.action}] -> ${n.target}`))," "),r.skipped>0&&e.onConflict==="skip"&&o.push("",t.yellow("note: existing files were kept. rerun with --on-conflict overwrite (replace)"),t.yellow(" or --on-conflict rename (park the incoming copy next to yours).")),e.manifest.warnings.length>0){let n=ek(e.manifest.redacted.length),i=e.manifest.warnings.filter(s=>s!==n);if(i.length>0){o.push("",t.yellow("export-time warnings:"));for(let s of i)o.push(` - ${t.yellow(s)}`)}}return e.unlisted.length>0&&o.push("",t.yellow(`note: ${e.unlisted.length} file(s) in the bundle are not listed in manifest.json and were NOT imported:`),` ${xt(e.unlisted)}`),o.push("",t.next(`${t.code("aforge detect")} (refresh this machine toolchain snapshot)`),` ${t.code("aforge sync")} (project the imported SoT to your agents)`),RB(o,e,t),o.join(`
|
|
532
535
|
`)}a(jB,"renderImport");function aT(e){let t=e.command("bundle").description("export / import a whole SoT layer (migration, backup, new machine)");t.command("export").description("copy one SoT layer into a portable bundle directory (manifest + sot/)").requiredOption("--out <dir>","output directory (must be empty or missing)").option("--scope <scope>","SoT scope to export: project or user (default: effective scope)").option("--no-redact","keep MCP credentials as-is (default: replace values by a placeholder)").option("--keep-detected","keep habits.detected (default: dropped - it is a machine snapshot)").option("--json","machine-readable output (absolute paths) - Spec 6.2").action(async(r,o)=>{let n=await CB(Q(),{out:r.out,scope:Ut(r.scope),redact:r.redact,keepDetected:r.keepDetected===!0});if(J(o,r.json)){G(n);return}console.log(TB(n))}),t.command("import").description("write a bundle directory into one SoT layer (verifies sha256 before writing)").requiredOption("--from <dir>","bundle directory produced by `aforge bundle export`").option("--scope <scope>","SoT scope to write: project or user (default: AGF_SCOPE or project)").option("--on-conflict <policy>","existing target files: skip (default) | overwrite | rename").option("--json","machine-readable output (absolute paths) - Spec 6.2").action(async(r,o)=>{let n=await OB(Q(),{from:r.from,scope:Ut(r.scope),onConflict:AB(r.onConflict)});if(J(o,r.json)){G(n);return}console.log(jB(n))})}a(aT,"registerBundleCommand");import go from"node:path";H();var ok=["fnm","nvm","volta","mise","nodenv","asdf"],ik=["uv","poetry","pipenv","conda","pyenv","pdm","hatch","rye","mamba","virtualenv"],sk=["pnpm","bun","npm","yarn","deno"],DB=["cargo","rustup","rustc","clippy","rustfmt"],NB=["golang","go.mod","go.sum","gofmt","goimports","gopls"],LB=["maven","gradle","mvnw","gradlew","sdkman","jdk","java"],MB=["dotnet","nuget","msbuild","csproj","csharp"],zB=["turborepo","turbo","nx","lerna","rush.json","rushstack","changesets","pnpm-workspace","workspaces"],FB=["github actions","gitlab ci","azure pipelines","jenkins","circleci","travis","dependabot","husky","lint-staged","pre-commit","commitlint"],Oi=[{id:"rust",detectedKey:"rust",label:"rust toolchain",keywords:DB},{id:"go",detectedKey:"go",label:"go toolchain",keywords:NB},{id:"java",detectedKey:"java",label:"java toolchain",keywords:LB},{id:"dotnet",detectedKey:"dotnet",label:"dotnet toolchain",keywords:MB},{id:"monorepo",detectedKey:"monorepo",label:"monorepo tools",keywords:zB},{id:"ci",detectedKey:"ci",label:"ci / hooks",keywords:FB}],ak=[...new Set([...ok,...ik,...sk,...Oi.flatMap(e=>e.keywords)])],cT=/[A-Za-z0-9_]/;function UB(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}a(UB,"escapeRegExp");function lT(e){let t=e.trim(),r=t.split(/\s+/).map(UB).join("\\s+"),o=t.slice(0,1),n=t.slice(-1),i=cT.test(o)?"(?<![A-Za-z0-9_])":"",s=cT.test(n)?"(?![A-Za-z0-9_])":"";return new RegExp(`${i}${r}${s}`,"i")}a(lT,"keywordRegExp");var HB=new Map(ak.map(e=>[e,lT(e)]));function uT(e,t){return(HB.get(t)??lT(t)).test(e)}a(uT,"matchesKeyword");var ck=[{kind:"AGENTS.md",tool:"agents.md \u7EA6\u5B9A\uFF08opencode / codex / pi \u7B49\uFF09",basename:"agents.md"},{kind:"CLAUDE.md",tool:"Claude Code",basename:"claude.md"},{kind:"GEMINI.md",tool:"Gemini CLI",basename:"gemini.md"},{kind:"opencode.md",tool:"opencode",basename:"opencode.md"},{kind:".cursorrules",tool:"Cursor\uFF08\u65E7\u7248\u5355\u6587\u4EF6\uFF09",basename:".cursorrules"},{kind:".cursor/rules/*.mdc",tool:"Cursor\uFF08\u65B0\u7248\u89C4\u5219\u76EE\u5F55\uFF09",extension:".mdc",parentDirs:[".cursor","rules"],nestedUnderParents:!0},{kind:".windsurfrules",tool:"Windsurf",basename:".windsurfrules"},{kind:".github/copilot-instructions.md",tool:"GitHub Copilot",basename:"copilot-instructions.md",parentDirs:[".github"]}];function BB(e){let t=mr(e).split("/").filter(o=>o!=="").map(o=>o.toLowerCase());return{base:t.pop()??"",ancestors:t}}a(BB,"splitPathSegments");function qB(e,t,r){if(t.length>e.length)return!1;if(!r){let o=e.slice(e.length-t.length);return t.every((n,i)=>o[i]===n)}for(let o=0;o+t.length<=e.length;o+=1)if(t.every((n,i)=>e[o+i]===n))return!0;return!1}a(qB,"ancestorsMatch");function VB(e,t){return!(e.basename!==void 0&&t.base!==e.basename||e.extension!==void 0&&(t.base===e.extension||!t.base.endsWith(e.extension))||e.parentDirs!==void 0&&!qB(t.ancestors,e.parentDirs,e.nestedUnderParents===!0))}a(VB,"ruleMatches");function lk(e){let t=BB(e);if(t.base!=="")return ck.find(r=>VB(r,t))?.kind}a(lk,"identifyImportFile");function dT(){return ck.map(e=>e.kind)}a(dT,"supportedImportFileKinds");function uk(){return`\u652F\u6301\u7684\u6587\u4EF6\uFF08\u5927\u5C0F\u5199\u4E0D\u654F\u611F\uFF09\uFF1A${dT().join(" / ")}\uFF1B\u5E26\u76EE\u5F55\u524D\u7F00\u7684\u9700\u8981\u653E\u5728\u5BF9\u5E94\u76EE\u5F55\u4E0B`}a(uk,"supportedImportFileHint");function ZB(e){let t=e,r=zt(t);for(;r.hasMarkers;)t=r.before+r.after,r=zt(t);return t}a(ZB,"stripAllMarkerSections");function GB(e,t){let r=ak.filter(o=>uT(e,o));return{heading:t,content:e,toolchainHits:r}}a(GB,"makeBlock");function WB(e){let t=[],r=[],o=null,n=a(()=>{r.join("").trim()!==""&&t.push(GB(r.join(""),o)),r=[],o=null},"flush");for(let i of e.split(/(?<=\n)/)){let s=/^##[ \t]+([^\r\n]*)/.exec(i);s!==null?(n(),o=(s[1]??"").trim(),r=[i]):r.push(i)}return n(),t}a(WB,"splitMarkdownBlocks");function pT(e,t){for(let r of t)for(let o of e)if(o.toolchainHits.includes(r))return r}a(pT,"firstHit");function fT(e,t){return t.filter(r=>e.some(o=>o.toolchainHits.includes(r)))}a(fT,"allHits");function mT(e){return e.nodeManager!==void 0||e.pythonManager!==void 0||e.packageManagers.length>0||Object.keys(e.extraToolchains).length>0}a(mT,"hasAnySuggestion");function KB(e){let t={};for(let r of Oi){let o=fT(e,r.keywords);o.length>0&&(t[r.id]=o)}return t}a(KB,"collectExtraToolchains");function hT(e){let t=WB(ZB(e)),r=t.filter(n=>n.toolchainHits.length>0),o=t.filter(n=>n.toolchainHits.length===0);return{blocks:t,toolchainBlocks:r,customBlocks:o,suggestions:{nodeManager:pT(t,ok),pythonManager:pT(t,ik),packageManagers:fT(t,sk),extraToolchains:KB(t)}}}a(hT,"parseImportedFile");function JB(e){let t={};for(let r of Oi){let o=e.extraToolchains[r.id];o!==void 0&&o.length>0&&(t[r.detectedKey]=o.map(n=>({name:n,source:"import"})))}return t}a(JB,"extraDetectedEntries");function gT(e,t,r){return{source:"import",imported_from:t,imported_at:r,...e.nodeManager!==void 0?{node:{manager:e.nodeManager,source:"import"}}:{},...e.pythonManager!==void 0?{python:{manager:e.pythonManager,source:"import"}}:{},...e.packageManagers.length>0?{package_managers:e.packageManagers.map(o=>({name:o,source:"import"}))}:{},...JB(e)}}a(gT,"buildImportDetected");function yT(e){let t=a(r=>String(r).padStart(2,"0"),"pad");return[`${e.getUTCFullYear()}${t(e.getUTCMonth()+1)}${t(e.getUTCDate())}`,`${t(e.getUTCHours())}${t(e.getUTCMinutes())}${t(e.getUTCSeconds())}`].join("-")}a(yT,"importTimestamp");function vT(e){let t=e.map(r=>lr(r.content,"lf").trim()).filter(r=>r!=="").join(`
|
|
533
536
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zythegit/agentforge",
|
|
3
|
-
"version": "0.2.3-rc.
|
|
3
|
+
"version": "0.2.3-rc.3",
|
|
4
4
|
"description": "AgentForge — manage AI coding CLI rule projections (SoT → opencode/codex/claude/pi) from one source of truth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"emit-schema": "tsx src/schema/emit.ts && biome check --write --error-on-warnings schemas/",
|
|
35
35
|
"gen:version": "node scripts/gen-version.mjs",
|
|
36
36
|
"gen:version:check": "node scripts/gen-version.mjs --check",
|
|
37
|
+
"measure:init-timing": "node scripts/measure-init-timing.mjs",
|
|
37
38
|
"build": "npm run build:node && npm run build:bun",
|
|
38
39
|
"build:bun": "npm run gen:version && node scripts/build-bun.mjs",
|
|
39
40
|
"build:bun:all": "npm run gen:version && node scripts/build-bun.mjs --all",
|