add-coder 0.3.16 → 0.3.18
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/index.js +269 -37
- package/package.json +1 -1
- package/templates/.add-coder-src-hash.json +31 -24
- package/templates/adapters/claude/hooks/pre-tool-use.sh +6 -1
- package/templates/adapters/codex/hooks/pre-tool-use.sh +7 -1
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +6 -1
- package/templates/adapters/trae/hooks/pre-tool-use.sh +7 -1
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +6 -1
- package/templates/core/hooks/pre-tool-use.sh +7 -1
- package/templates/core/prisma/add.prisma +35 -0
- package/templates/core/rules/profiles/index.toml +12 -0
- package/templates/core/rules/profiles/machineserver-profile.md +22 -0
- package/templates/core/rules/profiles/webapp-profile.md +61 -0
- package/templates/core/rules/project_rules.md +6 -54
- package/templates/core/scripts/mcp-server/tools/audit.ts +23 -14
- package/templates/core/scripts/mcp-server/tools/context.ts +20 -2
- package/templates/core/scripts/mcp-server/tools/contract.ts +202 -0
- package/templates/core/scripts/mcp-server/tools/docs.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/hitl.ts +13 -5
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +29 -10
- package/templates/core/scripts/mcp-server/tools/plan.ts +5 -2
- package/templates/core/scripts/mcp-server/tools/quality.ts +2 -2
- package/templates/core/scripts/mcp-server/tools/registrar.ts +11 -0
- package/templates/core/scripts/mcp-server/tools/review.ts +2 -2
- package/templates/core/templates/collab-contract-template.md +203 -0
- package/templates/core/templates/collab-contract-template.schema.json +133 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -63,6 +63,7 @@ var AddCoderConfigSchema = z.object({
|
|
|
63
63
|
mcpServerCommand: z.string().default("tsx"),
|
|
64
64
|
agentAuditImport: z.string().default("@/lib/agent-audit-logger"),
|
|
65
65
|
magicDir: z.string(),
|
|
66
|
+
stack: z.string().optional(),
|
|
66
67
|
adapters: z.array(AdapterEnum).default([]),
|
|
67
68
|
overrides: z.record(z.string(), z.string()).default({})
|
|
68
69
|
});
|
|
@@ -79,6 +80,7 @@ var defaults = {
|
|
|
79
80
|
mcpServerCommand: "tsx",
|
|
80
81
|
agentAuditImport: "@/lib/agent-audit-logger",
|
|
81
82
|
magicDir: "",
|
|
83
|
+
stack: "",
|
|
82
84
|
adapters: [],
|
|
83
85
|
overrides: {}
|
|
84
86
|
};
|
|
@@ -182,7 +184,7 @@ async function selectFiles(projectRoot, files) {
|
|
|
182
184
|
process.stdout.write(renderList(items, selected, scrollIdx, VISIBLE));
|
|
183
185
|
}
|
|
184
186
|
draw();
|
|
185
|
-
return new Promise((
|
|
187
|
+
return new Promise((resolve9) => {
|
|
186
188
|
input.on("keypress", (_char, key) => {
|
|
187
189
|
if (!key) return;
|
|
188
190
|
if (key.name === "up") {
|
|
@@ -223,7 +225,7 @@ async function selectFiles(projectRoot, files) {
|
|
|
223
225
|
if (selected.has(idx)) result.set(relPath, content);
|
|
224
226
|
idx++;
|
|
225
227
|
}
|
|
226
|
-
|
|
228
|
+
resolve9(result);
|
|
227
229
|
});
|
|
228
230
|
});
|
|
229
231
|
}
|
|
@@ -314,7 +316,7 @@ var SYNC_PRISMA_CONFIG = {
|
|
|
314
316
|
{ label: "\u751F\u4EA7/CI\uFF08\u53EA\u5E94\u7528\u5DF2\u6709\u8FC1\u79FB\uFF09", cmd: "npx prisma migrate deploy" },
|
|
315
317
|
{ label: "\u4E0D\u786E\u5B9A\uFF1F\u5148\u67E5\u72B6\u6001", cmd: "npx prisma migrate status" }
|
|
316
318
|
],
|
|
317
|
-
P3005: { hint: "\u82E5\u62A5 P3005\uFF08\u5E93\u6709\u8868\u4F46\u65E0\u8FC1\u79FB\u8BB0\u5F55\uFF09= \u9700\u5148\u57FA\u7EBF\u5316:", cmd: "npx prisma migrate resolve --applied <\
|
|
319
|
+
P3005: { hint: "\u82E5\u62A5 P3005\uFF08\u5E93\u6709\u8868\u4F46\u65E0\u8FC1\u79FB\u8BB0\u5F55\uFF09= \u9700\u5148\u57FA\u7EBF\u5316:", cmd: "npx prisma migrate resolve --applied <\uFFFD\uFFFD\u79FB\u540D>\uFF0C\u518D\u6267\u884C deploy" },
|
|
318
320
|
UNMANAGED_ACTIONS: [
|
|
319
321
|
{ label: "\u9996\u6B21\u63A5\u5165\u8FC1\u79FB\u7BA1\u7406\uFF08\u7A7A\u5E93\uFF09", cmd: "npx prisma migrate dev --name init" },
|
|
320
322
|
{ label: "\u5E93\u5DF2\u6709\u8868\uFF08db push \u5EFA\u8FC7\uFF09", hint: "\u5148\u624B\u52A8\u57FA\u7EBF\u5316\uFF0C\u518D deploy:", steps: ["npx prisma migrate diff --from-empty --to-schema-datamodel <schema\u6587\u4EF6> --script", "\u5C06\u8F93\u51FA\u5199\u5165 prisma/migrations/<\u65F6\u95F4\u6233>_init/migration.sql", "npx prisma migrate resolve --applied <\u65F6\u95F4\u6233>_init"] }
|
|
@@ -400,7 +402,7 @@ function diffPrisma(basePath, targetPath) {
|
|
|
400
402
|
}
|
|
401
403
|
|
|
402
404
|
// src/core/renderer.ts
|
|
403
|
-
import { readFileSync as readFileSync4, readdirSync, statSync, existsSync as existsSync5 } from "fs";
|
|
405
|
+
import { readFileSync as readFileSync4, readdirSync, statSync, existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
404
406
|
import { join as join4, relative, dirname as dirname2 } from "path";
|
|
405
407
|
import { fileURLToPath } from "url";
|
|
406
408
|
import { parse } from "smol-toml";
|
|
@@ -418,6 +420,24 @@ var PLACEHOLDERS = {
|
|
|
418
420
|
"{{mcpServerCommand}}": "mcpServerCommand",
|
|
419
421
|
"{{agentAuditImport}}": "agentAuditImport"
|
|
420
422
|
};
|
|
423
|
+
function renderStackPlaceholders(result, config) {
|
|
424
|
+
const stackName = config.stack || "\u672A\u8BBE\u7F6E";
|
|
425
|
+
const stackProfile = config.stack ? `${config.stack}-profile.md` : "\u672A\u8BBE\u7F6E";
|
|
426
|
+
let refLine;
|
|
427
|
+
if (config.stack) {
|
|
428
|
+
refLine = [
|
|
429
|
+
`\u672C\u9879\u76EE\u7684\u6280\u672F\u6808\u7EA6\u675F\u7531 \`${config.magicDir}/rules/profiles/${stackProfile}\` \u5B9A\u4E49\uFF08\u7531 \`add-coder stack set\` \u7BA1\u7406\uFF09\u3002`,
|
|
430
|
+
`- **\u5F53\u524D\u6280\u672F\u6808**: \`${stackName}\`\uFF08${stackProfile} \u5DF2\u751F\u6548\uFF0CAI \u5FC5\u987B\u9075\u5B88\u5176\u4E2D\u5168\u90E8\u7EA6\u675F\uFF09`
|
|
431
|
+
].join("\n");
|
|
432
|
+
} else {
|
|
433
|
+
refLine = [
|
|
434
|
+
"\u672C\u9879\u76EE\u7684\u6280\u672F\u6808\u672A\u8BBE\u7F6E\uFF0C\u4E0D\u65BD\u52A0\u4EFB\u4F55\u6280\u672F\u6808\u5047\u8BBE\u3002",
|
|
435
|
+
"AI \u5FC5\u987B\u901A\u8FC7 `get_project_context` \u8BFB\u53D6\u9879\u76EE\u5B9E\u9645\u4EE3\u7801\u63A8\u65AD\u771F\u5B9E\u6280\u672F\u6808\uFF0C\u7981\u6B62\u5957\u7528\u6A21\u677F\u6216\u6848\u4F8B\u4E2D\u7684\u9ED8\u8BA4\u6280\u672F\u6808\u3002",
|
|
436
|
+
"\uFF08\u53EF\u7528 `add-coder stack set <name>` \u542F\u7528\u6280\u672F\u6808\u7EA6\u675F\uFF09"
|
|
437
|
+
].join("\n");
|
|
438
|
+
}
|
|
439
|
+
return result.replaceAll("{{stackReferenceLine}}", refLine).replaceAll("{{stackName}}", stackName).replaceAll("{{stackProfile}}", stackProfile);
|
|
440
|
+
}
|
|
421
441
|
function loadDpsThresholds() {
|
|
422
442
|
try {
|
|
423
443
|
const candidates = [
|
|
@@ -455,12 +475,44 @@ function render(content, config) {
|
|
|
455
475
|
result = result.replaceAll("{{dpsPass}}", thresholds.pass);
|
|
456
476
|
result = result.replaceAll("{{dpsWarn}}", thresholds.warn);
|
|
457
477
|
}
|
|
478
|
+
result = renderStackPlaceholders(result, config);
|
|
458
479
|
return result;
|
|
459
480
|
}
|
|
460
481
|
var TEMPLATES_ROOT = join4(__dirname, "../templates");
|
|
461
482
|
var CORE_DIR = join4(TEMPLATES_ROOT, "core");
|
|
462
483
|
var CORE_TARGET = ".add";
|
|
463
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set(["prisma"]);
|
|
484
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["prisma", "profiles"]);
|
|
485
|
+
function loadProfileRegistry() {
|
|
486
|
+
try {
|
|
487
|
+
const tomlPath = join4(CORE_DIR, "rules", "profiles", "index.toml");
|
|
488
|
+
if (!existsSync5(tomlPath)) return [];
|
|
489
|
+
const doc = parse(readFileSync4(tomlPath, "utf-8"));
|
|
490
|
+
return doc.profile || [];
|
|
491
|
+
} catch (e) {
|
|
492
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
493
|
+
console.warn(`[renderer] profile \u6CE8\u518C\u8868\u8BFB\u53D6\u5931\u8D25: ${errorMessage}`);
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function loadStack(projectRoot, magicDir) {
|
|
498
|
+
try {
|
|
499
|
+
const p = join4(projectRoot, magicDir, "stack.json");
|
|
500
|
+
if (!existsSync5(p)) return "";
|
|
501
|
+
const doc = JSON.parse(readFileSync4(p, "utf-8"));
|
|
502
|
+
return typeof doc.stack === "string" ? doc.stack : "";
|
|
503
|
+
} catch {
|
|
504
|
+
return "";
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function saveStack(projectRoot, magicDir, stack) {
|
|
508
|
+
const dir = join4(projectRoot, magicDir);
|
|
509
|
+
mkdirSync2(dir, { recursive: true });
|
|
510
|
+
writeFileSync2(
|
|
511
|
+
join4(dir, "stack.json"),
|
|
512
|
+
JSON.stringify({ stack, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
513
|
+
"utf-8"
|
|
514
|
+
);
|
|
515
|
+
}
|
|
464
516
|
function renderCore(config, dryRun) {
|
|
465
517
|
const files = /* @__PURE__ */ new Map();
|
|
466
518
|
function walk(dir, base) {
|
|
@@ -480,6 +532,17 @@ function renderCore(config, dryRun) {
|
|
|
480
532
|
}
|
|
481
533
|
}
|
|
482
534
|
walk(CORE_DIR, "");
|
|
535
|
+
if (config.stack) {
|
|
536
|
+
const registry = loadProfileRegistry();
|
|
537
|
+
const profile = registry.find((p) => p.name === config.stack);
|
|
538
|
+
if (profile) {
|
|
539
|
+
const src = join4(CORE_DIR, "rules", "profiles", profile.file);
|
|
540
|
+
if (existsSync5(src)) {
|
|
541
|
+
const rendered = render(readFileSync4(src, "utf-8"), config);
|
|
542
|
+
files.set(join4(".add", "rules", "profiles", profile.file), rendered);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
483
546
|
if (dryRun) {
|
|
484
547
|
console.log(`[dry-run] Core templates: ${files.size} files`);
|
|
485
548
|
}
|
|
@@ -565,7 +628,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
565
628
|
|
|
566
629
|
// src/caijuehub/strategies/prisma.strategy.ts
|
|
567
630
|
import { spawnSync } from "child_process";
|
|
568
|
-
import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync, writeFileSync as
|
|
631
|
+
import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
569
632
|
import { resolve as resolve3 } from "path";
|
|
570
633
|
var PRISMA_CONFIG = {
|
|
571
634
|
onMissing: "ask",
|
|
@@ -578,7 +641,7 @@ var PRISMA_CONFIG = {
|
|
|
578
641
|
};
|
|
579
642
|
function ensurePrismaConfig(projectRoot) {
|
|
580
643
|
const configPath = resolve3(projectRoot, "prisma.config.ts");
|
|
581
|
-
|
|
644
|
+
writeFileSync3(configPath, [
|
|
582
645
|
'import dotenv from "dotenv";',
|
|
583
646
|
'import { existsSync } from "fs";',
|
|
584
647
|
'for (const f of [".env.development.local", ".env.development", ".env.local", ".env"]) {',
|
|
@@ -603,7 +666,7 @@ function backupAddTables(projectRoot) {
|
|
|
603
666
|
timeout: 3e4
|
|
604
667
|
});
|
|
605
668
|
if (r.stdout.length > 0) {
|
|
606
|
-
|
|
669
|
+
writeFileSync3(bak, r.stdout, "utf-8");
|
|
607
670
|
console.log(`>>> \u5907\u4EFD ADD \u8868\u5230 ${bak}`);
|
|
608
671
|
return bak;
|
|
609
672
|
}
|
|
@@ -621,7 +684,7 @@ function runPrismaInit(projectRoot, provider, schemaPath) {
|
|
|
621
684
|
if (initResult.status !== 0 || !existsSync7(schemaPath)) {
|
|
622
685
|
console.log("prisma init \u5931\u8D25\uFF0C\u624B\u52A8\u521B\u5EFA schema.prisma ...");
|
|
623
686
|
const prismaDir = resolve3(projectRoot, "prisma");
|
|
624
|
-
if (!existsSync7(prismaDir))
|
|
687
|
+
if (!existsSync7(prismaDir)) mkdirSync3(prismaDir, { recursive: true });
|
|
625
688
|
const content = `generator client {
|
|
626
689
|
provider = "prisma-client-js"
|
|
627
690
|
}
|
|
@@ -630,11 +693,11 @@ datasource db {
|
|
|
630
693
|
provider = "${provider}"
|
|
631
694
|
}
|
|
632
695
|
`;
|
|
633
|
-
|
|
696
|
+
writeFileSync3(schemaPath, content, "utf-8");
|
|
634
697
|
const devEnvPath = resolve3(projectRoot, ".env.development");
|
|
635
698
|
if (!existsSync7(devEnvPath)) {
|
|
636
699
|
const defaultUrl = provider === "sqlite" ? 'DATABASE_URL="file:./data/dev.db"' : '# \u8BF7\u7F16\u8F91\u4E3A\u4F60\u7684\u6570\u636E\u5E93\u8FDE\u63A5\u4FE1\u606F\nDATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DB?schema=public"';
|
|
637
|
-
|
|
700
|
+
writeFileSync3(devEnvPath, defaultUrl + "\n", "utf-8");
|
|
638
701
|
console.log("\u5DF2\u521B\u5EFA .env.development");
|
|
639
702
|
}
|
|
640
703
|
return false;
|
|
@@ -650,7 +713,7 @@ function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
|
|
|
650
713
|
if (dbUrl) {
|
|
651
714
|
const existing = existsSync7(devEnvPath) ? readFileSync5(devEnvPath, "utf-8") : "";
|
|
652
715
|
if (!existing.includes("DATABASE_URL=")) {
|
|
653
|
-
|
|
716
|
+
writeFileSync3(devEnvPath, `${existing}${existing ? "\n" : ""}${dbUrl[0]}
|
|
654
717
|
`, "utf-8");
|
|
655
718
|
}
|
|
656
719
|
if (existsSync7(envPath)) unlinkSync(envPath);
|
|
@@ -758,7 +821,7 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
758
821
|
}
|
|
759
822
|
|
|
760
823
|
// src/cli/commands/init.ts
|
|
761
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
824
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync4, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
762
825
|
import { createHash } from "crypto";
|
|
763
826
|
import { resolve as resolve5 } from "path";
|
|
764
827
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -774,6 +837,12 @@ var MAGIC_DIR_MAP = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", tra
|
|
|
774
837
|
async function initCommand(options) {
|
|
775
838
|
const ctx = await prepare(options);
|
|
776
839
|
writeComposeEnv(ctx);
|
|
840
|
+
if (!options.dryRun) {
|
|
841
|
+
saveStack(ctx.projectRoot, ctx.magicDir, ctx.stack);
|
|
842
|
+
if (ctx.stack) console.log(`\u6280\u672F\u6808\u72B6\u6001\u5DF2\u5199\u5165 ${ctx.magicDir}/stack.json \u2192 ${ctx.stack}`);
|
|
843
|
+
} else if (ctx.stack) {
|
|
844
|
+
console.log(`[dry-run] \u5C06\u5199\u5165 ${ctx.magicDir}/stack.json \u2192 ${ctx.stack}`);
|
|
845
|
+
}
|
|
777
846
|
const result = await renderAndWrite(ctx);
|
|
778
847
|
await deployDatabase(ctx);
|
|
779
848
|
deployDocs(ctx);
|
|
@@ -791,10 +860,12 @@ async function resolveAdapter(projectRoot, specified) {
|
|
|
791
860
|
return detected;
|
|
792
861
|
}
|
|
793
862
|
console.log("\u672A\u68C0\u6D4B\u5230 IDE \u73AF\u5883");
|
|
794
|
-
const a = (await ask("\u8BF7\u9009\u62E9\u76EE\u6807 IDE: [1] Qoder [2] Claude [3] VS Code \u2192 ")).trim();
|
|
863
|
+
const a = (await ask("\u8BF7\u9009\u62E9\u76EE\u6807 IDE: [1] Qoder [2] Claude [3] VS Code [4] Trae [5] Codex \u2192 ")).trim();
|
|
795
864
|
if (a === "1" || a === "qoder") return "qoder";
|
|
796
865
|
if (a === "2" || a === "claude") return "claude";
|
|
797
866
|
if (a === "3" || a === "vscode") return "vscode";
|
|
867
|
+
if (a === "4" || a === "trae") return "trae";
|
|
868
|
+
if (a === "5" || a === "codex") return "codex";
|
|
798
869
|
console.log("\u8F93\u5165\u65E0\u6CD5\u8BC6\u522B\uFF0C\u9ED8\u8BA4 qoder");
|
|
799
870
|
return "qoder";
|
|
800
871
|
}
|
|
@@ -938,8 +1009,8 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
938
1009
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
939
1010
|
return;
|
|
940
1011
|
}
|
|
941
|
-
if (!existsSync8(scriptsDir))
|
|
942
|
-
|
|
1012
|
+
if (!existsSync8(scriptsDir)) mkdirSync4(scriptsDir, { recursive: true });
|
|
1013
|
+
writeFileSync4(scriptPath, content, "utf-8");
|
|
943
1014
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
944
1015
|
}
|
|
945
1016
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
@@ -953,10 +1024,26 @@ function injectDbExportScript(projectRoot, dryRun) {
|
|
|
953
1024
|
if (!pkg.scripts) pkg.scripts = {};
|
|
954
1025
|
if (!pkg.scripts["db:export"]) {
|
|
955
1026
|
pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
|
|
956
|
-
|
|
1027
|
+
writeFileSync4(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
957
1028
|
console.log("\u5DF2\u5728 package.json \u6CE8\u5165 db:export");
|
|
958
1029
|
}
|
|
959
1030
|
}
|
|
1031
|
+
async function resolveStack(options) {
|
|
1032
|
+
if (options.stack) {
|
|
1033
|
+
console.log(`\u6280\u672F\u6808\u7EA6\u675F: ${options.stack} (--stack)`);
|
|
1034
|
+
return options.stack;
|
|
1035
|
+
}
|
|
1036
|
+
if (options.force) return "";
|
|
1037
|
+
console.log(["", "\u6280\u672F\u6808\u7EA6\u675F\uFF08\u53EF\u9009\uFF09:", " [1] \u4E0D\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF0C\u63A8\u8350\uFF09", " [2] webapp", " [3] machineserver", " [4] \u81EA\u5B9A\u4E49\uFF08\u8F93\u5165 profile \u540D\uFF09"].join("\n"));
|
|
1038
|
+
const a = (await ask("\u8BF7\u9009\u62E9 [1/2/3/4] \u2192 ")).trim();
|
|
1039
|
+
if (a === "2" || a === "webapp") return "webapp";
|
|
1040
|
+
if (a === "3" || a === "machineserver") return "machineserver";
|
|
1041
|
+
if (a === "4" || a === "custom") {
|
|
1042
|
+
const name = (await ask("\u81EA\u5B9A\u4E49 profile \u540D\uFF08\u9700\u5DF2\u5728 .qoder/rules/profiles/ \u5B58\u5728\uFF09: ")).trim();
|
|
1043
|
+
return name || "";
|
|
1044
|
+
}
|
|
1045
|
+
return "";
|
|
1046
|
+
}
|
|
960
1047
|
async function prepare(options) {
|
|
961
1048
|
const projectRoot = process.cwd();
|
|
962
1049
|
const target = await resolveAdapter(projectRoot, options.adapter);
|
|
@@ -964,6 +1051,8 @@ async function prepare(options) {
|
|
|
964
1051
|
const config = await loadConfig(projectRoot, options.config, { force: options.force });
|
|
965
1052
|
config.projectRoot = projectRoot;
|
|
966
1053
|
config.magicDir = magicDir;
|
|
1054
|
+
const stack = await resolveStack(options);
|
|
1055
|
+
config.stack = stack;
|
|
967
1056
|
const db = await resolveDbEngine(!!options.force);
|
|
968
1057
|
if (db.engine === "postgresql") {
|
|
969
1058
|
db.container = await resolveContainer(!!options.force);
|
|
@@ -971,7 +1060,7 @@ async function prepare(options) {
|
|
|
971
1060
|
Object.assign(db, await resolveDbCredentials(!!options.force));
|
|
972
1061
|
}
|
|
973
1062
|
}
|
|
974
|
-
return { projectRoot, options, target, magicDir, config, db };
|
|
1063
|
+
return { projectRoot, options, target, magicDir, config, db, stack };
|
|
975
1064
|
}
|
|
976
1065
|
function writeComposeEnv(ctx) {
|
|
977
1066
|
const { projectRoot, options, config, db } = ctx;
|
|
@@ -980,7 +1069,7 @@ function writeComposeEnv(ctx) {
|
|
|
980
1069
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
981
1070
|
const composePath = resolve5(projectRoot, composeName);
|
|
982
1071
|
if (!options.dryRun && (!existsSync8(composePath) || options.force)) {
|
|
983
|
-
|
|
1072
|
+
writeFileSync4(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
984
1073
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
985
1074
|
}
|
|
986
1075
|
}
|
|
@@ -988,7 +1077,7 @@ function writeComposeEnv(ctx) {
|
|
|
988
1077
|
if (!options.dryRun && existsSync8(devEnvPath)) {
|
|
989
1078
|
const existing = readFileSync6(devEnvPath, "utf-8");
|
|
990
1079
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
991
|
-
|
|
1080
|
+
writeFileSync4(devEnvPath, existing + `
|
|
992
1081
|
DATABASE_USER=${db.user || "admin"}
|
|
993
1082
|
DATABASE_PASSWORD=${db.password || "change-me-in-production"}
|
|
994
1083
|
DATABASE_PORT=${db.port || "5433"}
|
|
@@ -1030,7 +1119,7 @@ async function renderAndWrite(ctx) {
|
|
|
1030
1119
|
if (dry) {
|
|
1031
1120
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
1032
1121
|
} else {
|
|
1033
|
-
|
|
1122
|
+
mkdirSync4(reviewsDir, { recursive: true });
|
|
1034
1123
|
}
|
|
1035
1124
|
}
|
|
1036
1125
|
}
|
|
@@ -1045,9 +1134,9 @@ async function renderAndWrite(ctx) {
|
|
|
1045
1134
|
hashMap[rp] = createHash("sha256").update(c).digest("hex").slice(0, 8);
|
|
1046
1135
|
}
|
|
1047
1136
|
const hashOut = resolve5(projectRoot, magicDir, ".add-coder-hash.json");
|
|
1048
|
-
|
|
1137
|
+
writeFileSync4(hashOut, JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
1049
1138
|
if (npmVer) {
|
|
1050
|
-
|
|
1139
|
+
writeFileSync4(resolve5(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
|
|
1051
1140
|
}
|
|
1052
1141
|
console.log(`hash: ${Object.keys(hashMap).length} entries \u2192 ${magicDir}/.add-coder-hash.json`);
|
|
1053
1142
|
}
|
|
@@ -1111,7 +1200,7 @@ function deployDocs(ctx) {
|
|
|
1111
1200
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
1112
1201
|
const srcDir = resolve5(groundingSrc, d);
|
|
1113
1202
|
const destDir = resolve5(docsBase, d);
|
|
1114
|
-
if (!existsSync8(destDir))
|
|
1203
|
+
if (!existsSync8(destDir)) mkdirSync4(destDir, { recursive: true });
|
|
1115
1204
|
if (!existsSync8(srcDir)) continue;
|
|
1116
1205
|
for (const f of readdirSync2(srcDir)) {
|
|
1117
1206
|
const src = resolve5(srcDir, f);
|
|
@@ -1144,13 +1233,13 @@ function finalize(ctx, result) {
|
|
|
1144
1233
|
}
|
|
1145
1234
|
|
|
1146
1235
|
// src/cli/commands/sync.ts
|
|
1147
|
-
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as
|
|
1236
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
1148
1237
|
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
1149
1238
|
import { createHash as createHash2 } from "crypto";
|
|
1150
1239
|
|
|
1151
1240
|
// src/caijuehub/strategies/sync.strategy.ts
|
|
1152
1241
|
var SYNC_CONFIG = {
|
|
1153
|
-
PATCH_GUARD: [/[/]plans[/]/, /[/]specs[/]/, /[/]reviews[/]/],
|
|
1242
|
+
PATCH_GUARD: [/[/]plans[/]/, /[/]specs[/]/, /[/]reviews[/]/, /[/]rules[/]profiles[/]/],
|
|
1154
1243
|
HASH_OUTPUT_FILE: ".add-coder-hash.json",
|
|
1155
1244
|
HASH_SRC_FILE: "templates/.add-coder-src-hash.json",
|
|
1156
1245
|
HASH_HEX_LENGTH: 8,
|
|
@@ -1211,12 +1300,12 @@ function loadVersionFile(root, magic) {
|
|
|
1211
1300
|
}
|
|
1212
1301
|
}
|
|
1213
1302
|
function saveVersionFile(root, magic, version2) {
|
|
1214
|
-
|
|
1303
|
+
writeFileSync5(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
|
|
1215
1304
|
}
|
|
1216
1305
|
function saveHashFile(root, magic, files) {
|
|
1217
1306
|
const m = {};
|
|
1218
1307
|
for (const [p, c] of files) m[p] = hash8(c);
|
|
1219
|
-
|
|
1308
|
+
writeFileSync5(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
|
|
1220
1309
|
}
|
|
1221
1310
|
async function syncCommand(options = {}) {
|
|
1222
1311
|
const projectRoot = process.cwd();
|
|
@@ -1497,8 +1586,8 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
1497
1586
|
} else {
|
|
1498
1587
|
console.log(`
|
|
1499
1588
|
\u76EE\u6807 schema \u6587\u4EF6\u4E0D\u5B58\u5728: ${result.targetPath}`);
|
|
1500
|
-
|
|
1501
|
-
|
|
1589
|
+
mkdirSync5(dirname4(targetPath), { recursive: true });
|
|
1590
|
+
writeFileSync5(targetPath, "// add.prisma \u2014 ADD \u6CBB\u7406\u6A21\u578B\n\n", "utf-8");
|
|
1502
1591
|
const n = injectMissingModels(targetPath, result.missing);
|
|
1503
1592
|
console.log(` \u2705 \u5DF2\u521B\u5EFA\u5E76\u6CE8\u5165 ${n} \u4E2A\u6A21\u578B/\u679A\u4E3E`);
|
|
1504
1593
|
if (n > 0) {
|
|
@@ -1538,7 +1627,7 @@ function injectMissingModels(targetPath, models) {
|
|
|
1538
1627
|
for (const m of models) {
|
|
1539
1628
|
content += m.body + "\n\n";
|
|
1540
1629
|
}
|
|
1541
|
-
|
|
1630
|
+
writeFileSync5(targetPath, content, "utf-8");
|
|
1542
1631
|
return models.length;
|
|
1543
1632
|
}
|
|
1544
1633
|
function getBaseFieldLines(basePath, modelName) {
|
|
@@ -1573,7 +1662,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
1573
1662
|
${fieldLine}$3`);
|
|
1574
1663
|
count++;
|
|
1575
1664
|
}
|
|
1576
|
-
if (count > 0)
|
|
1665
|
+
if (count > 0) writeFileSync5(targetPath, content, "utf-8");
|
|
1577
1666
|
return count;
|
|
1578
1667
|
}
|
|
1579
1668
|
function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
@@ -1590,7 +1679,7 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
|
1590
1679
|
content = content.replace(fieldRegex, `${match[1]}${baseLine}`);
|
|
1591
1680
|
count++;
|
|
1592
1681
|
}
|
|
1593
|
-
if (count > 0)
|
|
1682
|
+
if (count > 0) writeFileSync5(targetPath, content, "utf-8");
|
|
1594
1683
|
return count;
|
|
1595
1684
|
}
|
|
1596
1685
|
|
|
@@ -1621,13 +1710,156 @@ async function statusCommand() {
|
|
|
1621
1710
|
}
|
|
1622
1711
|
}
|
|
1623
1712
|
|
|
1713
|
+
// src/cli/commands/stack.ts
|
|
1714
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
1715
|
+
import { resolve as resolve8, join as join5 } from "path";
|
|
1716
|
+
import { createHash as createHash3 } from "crypto";
|
|
1717
|
+
var MAGIC_DIR_MAP3 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
|
|
1718
|
+
var HASH_OUTPUT_FILE = ".add-coder-hash.json";
|
|
1719
|
+
function hash82(c) {
|
|
1720
|
+
return createHash3("sha256").update(c).digest("hex").slice(0, 8);
|
|
1721
|
+
}
|
|
1722
|
+
function resolveMagicDir(projectRoot, specified) {
|
|
1723
|
+
if (specified) {
|
|
1724
|
+
if (!MAGIC_DIR_MAP3[specified]) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
|
|
1725
|
+
return MAGIC_DIR_MAP3[specified];
|
|
1726
|
+
}
|
|
1727
|
+
const detected = detectIDE2(projectRoot);
|
|
1728
|
+
const adapter = detected !== "auto" ? detected : "qoder";
|
|
1729
|
+
return MAGIC_DIR_MAP3[adapter];
|
|
1730
|
+
}
|
|
1731
|
+
function listCustomProfiles(projectRoot, magicDir, registryNames) {
|
|
1732
|
+
const dir = resolve8(projectRoot, magicDir, "rules", "profiles");
|
|
1733
|
+
if (!existsSync11(dir)) return [];
|
|
1734
|
+
return readdirSync3(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
|
|
1735
|
+
}
|
|
1736
|
+
function profileExists(projectRoot, magicDir, name) {
|
|
1737
|
+
const registry = loadProfileRegistry();
|
|
1738
|
+
if (registry.some((p) => p.name === name)) return true;
|
|
1739
|
+
return existsSync11(resolve8(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
|
|
1740
|
+
}
|
|
1741
|
+
function stackCommand(sub, name, options = {}) {
|
|
1742
|
+
const projectRoot = process.cwd();
|
|
1743
|
+
const magicDir = resolveMagicDir(projectRoot, options.adapter);
|
|
1744
|
+
const registry = loadProfileRegistry();
|
|
1745
|
+
if (options.clear) {
|
|
1746
|
+
saveStack(projectRoot, magicDir, "");
|
|
1747
|
+
console.log(`\u5DF2\u6E05\u9664\u6280\u672F\u6808\u8BBE\u7F6E\uFF08${magicDir}/stack.json \u2192 \u4E2D\u6027\uFF09`);
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
if (sub === "list" || !sub) {
|
|
1751
|
+
const current = loadStack(projectRoot, magicDir);
|
|
1752
|
+
console.log("\u6280\u672F\u6808 profile \u5217\u8868:");
|
|
1753
|
+
console.log(" \u5185\u7F6E\uFF08\u6CE8\u518C\u8868\uFF09:");
|
|
1754
|
+
for (const p of registry) {
|
|
1755
|
+
const mark = p.name === current ? " \u2190 \u5F53\u524D" : "";
|
|
1756
|
+
console.log(` ${p.name} \u2014 ${p.description}${mark}`);
|
|
1757
|
+
}
|
|
1758
|
+
const custom = listCustomProfiles(projectRoot, magicDir, new Set(registry.map((p) => p.name)));
|
|
1759
|
+
if (custom.length > 0) {
|
|
1760
|
+
console.log(" \u81EA\u5B9A\u4E49\uFF08\u9879\u76EE\u4FA7\uFF09:");
|
|
1761
|
+
for (const c of custom) {
|
|
1762
|
+
const name2 = c.replace(/-profile\.md$/, "");
|
|
1763
|
+
const mark = name2 === current ? " \u2190 \u5F53\u524D" : "";
|
|
1764
|
+
console.log(` ${name2}${mark}`);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (!current) console.log("\n\u5F53\u524D: \u672A\u8BBE\u7F6E\uFF08\u4E0D\u65BD\u52A0\u4EFB\u4F55\u6280\u672F\u6808\u5047\u8BBE\uFF09");
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
if (sub === "show") {
|
|
1771
|
+
const current = loadStack(projectRoot, magicDir);
|
|
1772
|
+
if (!current) {
|
|
1773
|
+
console.log("\u6280\u672F\u6808: \u672A\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF0C\u65E0\u6280\u672F\u6808\u5047\u8BBE\uFF09");
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
const profilePath = resolve8(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
|
|
1777
|
+
const stat = existsSync11(profilePath) ? readFileSync8(profilePath, "utf-8").length : 0;
|
|
1778
|
+
console.log(`\u6280\u672F\u6808: ${current}`);
|
|
1779
|
+
console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync11(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
|
|
1780
|
+
try {
|
|
1781
|
+
const raw = JSON.parse(readFileSync8(resolve8(projectRoot, magicDir, "stack.json"), "utf-8"));
|
|
1782
|
+
if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (sub === "set") {
|
|
1788
|
+
if (!name) {
|
|
1789
|
+
console.log("\u7528\u6CD5: add-coder stack set <name>\uFF08\u5185\u7F6E: " + registry.map((p) => p.name).join(" | ") + "\uFF0C\u6216\u81EA\u5B9A\u4E49 profile \u540D\uFF09");
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
applyStack(projectRoot, magicDir, name);
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
console.log(`\u672A\u77E5\u5B50\u547D\u4EE4: ${sub}\uFF08\u53EF\u7528: list / set <name> / show / --clear\uFF09`);
|
|
1796
|
+
}
|
|
1797
|
+
function buildConfig(projectRoot, magicDir, stack) {
|
|
1798
|
+
const config = {
|
|
1799
|
+
...defaults,
|
|
1800
|
+
projectName: "add-project",
|
|
1801
|
+
projectRoot,
|
|
1802
|
+
magicDir,
|
|
1803
|
+
stack
|
|
1804
|
+
};
|
|
1805
|
+
try {
|
|
1806
|
+
const pkgPath = resolve8(projectRoot, "package.json");
|
|
1807
|
+
if (existsSync11(pkgPath)) {
|
|
1808
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
1809
|
+
if (pkg.name) config.projectName = pkg.name;
|
|
1810
|
+
}
|
|
1811
|
+
} catch {
|
|
1812
|
+
}
|
|
1813
|
+
return config;
|
|
1814
|
+
}
|
|
1815
|
+
function applyStack(projectRoot, magicDir, name) {
|
|
1816
|
+
if (!profileExists(projectRoot, magicDir, name)) {
|
|
1817
|
+
const registry = loadProfileRegistry();
|
|
1818
|
+
console.error(`\u2717 profile \u4E0D\u5B58\u5728: ${name}`);
|
|
1819
|
+
console.error(` \u5185\u7F6E: ${registry.map((p) => p.name).join(" | ")}\uFF08\u6216\u81EA\u5B9A\u4E49: ${magicDir}/rules/profiles/{name}-profile.md\uFF09`);
|
|
1820
|
+
process.exit(1);
|
|
1821
|
+
}
|
|
1822
|
+
const config = buildConfig(projectRoot, magicDir, name);
|
|
1823
|
+
saveStack(projectRoot, magicDir, name);
|
|
1824
|
+
const coreFiles = renderCore(config, false);
|
|
1825
|
+
const stackRelated = /* @__PURE__ */ new Map();
|
|
1826
|
+
for (const [relPath, content] of coreFiles) {
|
|
1827
|
+
if (relPath.includes("/rules/profiles/") || relPath.endsWith("/rules/project_rules.md")) {
|
|
1828
|
+
stackRelated.set(relPath, content);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
const hashMap = {};
|
|
1832
|
+
try {
|
|
1833
|
+
Object.assign(hashMap, JSON.parse(readFileSync8(resolve8(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
|
|
1834
|
+
} catch {
|
|
1835
|
+
}
|
|
1836
|
+
let written = 0;
|
|
1837
|
+
for (const [relPath, content] of stackRelated) {
|
|
1838
|
+
for (const t of [".add", magicDir]) {
|
|
1839
|
+
const targetPath = resolve8(projectRoot, relPath.replace(/^\.add/, t));
|
|
1840
|
+
mkdirSync6(join5(targetPath, ".."), { recursive: true });
|
|
1841
|
+
writeFileSync6(targetPath, content, "utf-8");
|
|
1842
|
+
hashMap[relPath.replace(/^\.add/, t)] = hash82(content);
|
|
1843
|
+
written++;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
writeFileSync6(resolve8(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
1847
|
+
console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
|
|
1848
|
+
console.log(` ${magicDir}/stack.json \u2192 ${name}`);
|
|
1849
|
+
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync11(resolve8(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
|
|
1850
|
+
console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1624
1853
|
// src/cli/index.ts
|
|
1625
1854
|
var { version } = JSON.parse(
|
|
1626
|
-
|
|
1855
|
+
readFileSync9(new URL("../package.json", import.meta.url), "utf-8")
|
|
1627
1856
|
);
|
|
1628
1857
|
var program = new Command();
|
|
1629
1858
|
program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
|
|
1630
|
-
program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").action(initCommand);
|
|
1631
|
-
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").action(syncCommand);
|
|
1859
|
+
program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").action(initCommand);
|
|
1860
|
+
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").action(syncCommand);
|
|
1632
1861
|
program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
|
|
1862
|
+
program.command("stack").description("\u7BA1\u7406\u6280\u672F\u6808\u7EA6\u675F profile\uFF08list / set <name> / show / --clear\uFF09").argument("[sub]", "list | set | show").argument("[name]", "set <name>: profile \u540D\uFF08\u5185\u7F6E\u6216\u81EA\u5B9A\u4E49\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--clear", "\u6E05\u9664\u6280\u672F\u6808\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF09").action(
|
|
1863
|
+
(sub, name, options) => stackCommand(sub, name, options)
|
|
1864
|
+
);
|
|
1633
1865
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-coder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "[codein2027](https://github.com/xiaomingming92/codein2027) - A complete scaffolding for building the ADD programming paradigm, the implementation layer for AI code governance. Core principle: Audit as Infrastructure. Breaks the black-box programming process and cross-session amnesia, evolving programming paradigms into an auditable, traceable, and convergent new era. npx-ready.\n\n[codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架——AI 代码治理的落地方案。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。npx 即用,人人可体验。",
|
|
6
6
|
"repository": {
|