aact 3.0.0-beta.4 → 3.0.0-beta.5
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/cli/index.mjs +296 -2
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -7,8 +7,10 @@ import path, { basename } from 'pathe';
|
|
|
7
7
|
import * as v from 'valibot';
|
|
8
8
|
import fs, { readFile, writeFile } from 'node:fs/promises';
|
|
9
9
|
import { colors, box } from 'consola/utils';
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import os from 'node:os';
|
|
10
12
|
|
|
11
|
-
const version = "3.0.0-beta.
|
|
13
|
+
const version = "3.0.0-beta.5";
|
|
12
14
|
|
|
13
15
|
const matchesPattern = (filePath, pattern) => {
|
|
14
16
|
if (pattern.startsWith("*")) {
|
|
@@ -708,12 +710,304 @@ const rule = defineCommand({
|
|
|
708
710
|
subCommands: { list: listAction }
|
|
709
711
|
});
|
|
710
712
|
|
|
713
|
+
const skillName = "aact-architect";
|
|
714
|
+
const markerFileName = ".aact-skill.json";
|
|
715
|
+
const defaultRepo = "https://github.com/ChS23/aact-architect-skill.git";
|
|
716
|
+
const defaultRef = "main";
|
|
717
|
+
const clientValues = [
|
|
718
|
+
"shared",
|
|
719
|
+
"codex",
|
|
720
|
+
"cursor",
|
|
721
|
+
"copilot",
|
|
722
|
+
"claude",
|
|
723
|
+
"cline",
|
|
724
|
+
"all"
|
|
725
|
+
];
|
|
726
|
+
const targetLabels = {
|
|
727
|
+
shared: "Agent Skills",
|
|
728
|
+
claude: "Claude Code",
|
|
729
|
+
cline: "Cline"
|
|
730
|
+
};
|
|
731
|
+
const defaultRoots = {
|
|
732
|
+
shared: "~/.agents/skills",
|
|
733
|
+
claude: "~/.claude/skills",
|
|
734
|
+
cline: "~/.cline/skills"
|
|
735
|
+
};
|
|
736
|
+
const sharedClientValues = /* @__PURE__ */ new Set([
|
|
737
|
+
"shared",
|
|
738
|
+
"codex",
|
|
739
|
+
"cursor",
|
|
740
|
+
"copilot"
|
|
741
|
+
]);
|
|
742
|
+
const defaultRuntime = {
|
|
743
|
+
git: (args, options) => new Promise((resolve, reject) => {
|
|
744
|
+
execFile(
|
|
745
|
+
// eslint-disable-next-line sonarjs/no-os-command-from-path -- This CLI intentionally invokes the user's git binary to clone/update the skill repository.
|
|
746
|
+
"git",
|
|
747
|
+
[...args],
|
|
748
|
+
{ cwd: options?.cwd },
|
|
749
|
+
(error, _stdout, stderr) => {
|
|
750
|
+
if (error) {
|
|
751
|
+
const reason = stderr.trim() || error.message;
|
|
752
|
+
reject(new Error(`git ${args.join(" ")} failed: ${reason}`));
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
resolve();
|
|
756
|
+
}
|
|
757
|
+
);
|
|
758
|
+
}),
|
|
759
|
+
now: () => /* @__PURE__ */ new Date()
|
|
760
|
+
};
|
|
761
|
+
const isClientValue = (value) => clientValues.includes(value);
|
|
762
|
+
const expandHome = (input) => {
|
|
763
|
+
if (input === "~") return os.homedir();
|
|
764
|
+
if (input.startsWith("~/")) return path.join(os.homedir(), input.slice(2));
|
|
765
|
+
return input;
|
|
766
|
+
};
|
|
767
|
+
const resolveRootDir = (rootOrSkillDir) => {
|
|
768
|
+
const resolved = path.resolve(expandHome(rootOrSkillDir));
|
|
769
|
+
return path.basename(resolved) === skillName ? path.dirname(resolved) : resolved;
|
|
770
|
+
};
|
|
771
|
+
const toSkillDir = (rootDir) => path.join(rootDir, skillName);
|
|
772
|
+
const normalizeKind = (client) => {
|
|
773
|
+
if (client === "all") return ["shared", "claude", "cline"];
|
|
774
|
+
if (sharedClientValues.has(client)) return ["shared"];
|
|
775
|
+
if (client === "claude" || client === "cline") return [client];
|
|
776
|
+
return ["shared"];
|
|
777
|
+
};
|
|
778
|
+
const selectedKinds = (args) => {
|
|
779
|
+
const out = /* @__PURE__ */ new Set();
|
|
780
|
+
if (args.all) {
|
|
781
|
+
for (const kind of normalizeKind("all")) out.add(kind);
|
|
782
|
+
}
|
|
783
|
+
if (args.client) {
|
|
784
|
+
if (!isClientValue(args.client)) {
|
|
785
|
+
throw new Error(
|
|
786
|
+
`Unknown skill client "${args.client}". Expected one of: ${clientValues.join(", ")}.`
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
for (const kind of normalizeKind(args.client)) out.add(kind);
|
|
790
|
+
}
|
|
791
|
+
if (args.codex) out.add("shared");
|
|
792
|
+
if (args.cursor) out.add("shared");
|
|
793
|
+
if (args.copilot) out.add("shared");
|
|
794
|
+
if (args.claude) out.add("claude");
|
|
795
|
+
if (args.cline) out.add("cline");
|
|
796
|
+
if (out.size === 0) out.add("shared");
|
|
797
|
+
return [...out];
|
|
798
|
+
};
|
|
799
|
+
const createInstallPlans = (args) => {
|
|
800
|
+
const kinds = selectedKinds(args);
|
|
801
|
+
if (args.target && kinds.length > 1) {
|
|
802
|
+
throw new Error(
|
|
803
|
+
"--target can be used with a single client target only. Remove --all or install clients one by one."
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
return kinds.map((kind) => {
|
|
807
|
+
const rootDir = resolveRootDir(args.target ?? defaultRoots[kind]);
|
|
808
|
+
return {
|
|
809
|
+
kind,
|
|
810
|
+
label: targetLabels[kind],
|
|
811
|
+
rootDir,
|
|
812
|
+
skillDir: toSkillDir(rootDir)
|
|
813
|
+
};
|
|
814
|
+
});
|
|
815
|
+
};
|
|
816
|
+
const pathExists = async (target) => {
|
|
817
|
+
try {
|
|
818
|
+
await fs.access(target);
|
|
819
|
+
return true;
|
|
820
|
+
} catch {
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
const readMarker = async (skillDir) => {
|
|
825
|
+
try {
|
|
826
|
+
const content = await fs.readFile(
|
|
827
|
+
path.join(skillDir, markerFileName),
|
|
828
|
+
"utf8"
|
|
829
|
+
);
|
|
830
|
+
const parsed = JSON.parse(content);
|
|
831
|
+
if (parsed.managedBy === "aact" && parsed.skill === skillName) {
|
|
832
|
+
return parsed;
|
|
833
|
+
}
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
};
|
|
838
|
+
const writeMarker = async (plan, repo, ref, runtime) => {
|
|
839
|
+
const marker = {
|
|
840
|
+
managedBy: "aact",
|
|
841
|
+
skill: skillName,
|
|
842
|
+
client: plan.kind,
|
|
843
|
+
repo,
|
|
844
|
+
ref,
|
|
845
|
+
aactVersion: version,
|
|
846
|
+
installedAt: runtime.now().toISOString()
|
|
847
|
+
};
|
|
848
|
+
await fs.writeFile(
|
|
849
|
+
path.join(plan.skillDir, markerFileName),
|
|
850
|
+
JSON.stringify(marker, void 0, 2) + "\n"
|
|
851
|
+
);
|
|
852
|
+
};
|
|
853
|
+
const ensureSkillFile = async (skillDir) => {
|
|
854
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
855
|
+
if (!await pathExists(skillFile)) {
|
|
856
|
+
throw new Error(
|
|
857
|
+
`Installed repository does not contain ${skillName}/SKILL.md at ${skillFile}.`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
const cloneSkill = async (plan, repo, ref, runtime) => {
|
|
862
|
+
await fs.mkdir(plan.rootDir, { recursive: true });
|
|
863
|
+
await runtime.git([
|
|
864
|
+
"clone",
|
|
865
|
+
"--depth",
|
|
866
|
+
"1",
|
|
867
|
+
"--branch",
|
|
868
|
+
ref,
|
|
869
|
+
repo,
|
|
870
|
+
plan.skillDir
|
|
871
|
+
]);
|
|
872
|
+
await ensureSkillFile(plan.skillDir);
|
|
873
|
+
};
|
|
874
|
+
const updateSkill = async (plan, repo, ref, runtime) => {
|
|
875
|
+
const marker = await readMarker(plan.skillDir);
|
|
876
|
+
if (!marker) {
|
|
877
|
+
throw new Error(
|
|
878
|
+
`${plan.skillDir} already exists and is not managed by aact. Use --force to overwrite it.`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (marker.repo !== repo) {
|
|
882
|
+
throw new Error(
|
|
883
|
+
`${plan.skillDir} is managed by aact but was installed from ${marker.repo}. Use --force to reinstall from ${repo}.`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
if (!await pathExists(path.join(plan.skillDir, ".git"))) {
|
|
887
|
+
throw new Error(
|
|
888
|
+
`${plan.skillDir} is managed by aact but is not a git checkout. Use --force to reinstall it.`
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
await runtime.git(["fetch", "--depth", "1", "origin", ref], {
|
|
892
|
+
cwd: plan.skillDir
|
|
893
|
+
});
|
|
894
|
+
await runtime.git(["checkout", "--force", "FETCH_HEAD"], {
|
|
895
|
+
cwd: plan.skillDir
|
|
896
|
+
});
|
|
897
|
+
await ensureSkillFile(plan.skillDir);
|
|
898
|
+
};
|
|
899
|
+
const installOne = async (plan, args, runtime) => {
|
|
900
|
+
const repo = args.repo ?? defaultRepo;
|
|
901
|
+
const ref = args.ref ?? defaultRef;
|
|
902
|
+
const dryRun = args["dry-run"] ?? false;
|
|
903
|
+
const force = args.force ?? false;
|
|
904
|
+
const exists = await pathExists(plan.skillDir);
|
|
905
|
+
const action = exists ? "update" : "install";
|
|
906
|
+
if (dryRun) {
|
|
907
|
+
consola.info(
|
|
908
|
+
`[dry run] ${force && exists ? "reinstall" : action} ${skillName} for ${plan.label}: ${plan.skillDir}`
|
|
909
|
+
);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
if (exists && force) {
|
|
913
|
+
await fs.rm(plan.skillDir, { recursive: true, force: true });
|
|
914
|
+
}
|
|
915
|
+
if (exists && !force) {
|
|
916
|
+
await updateSkill(plan, repo, ref, runtime);
|
|
917
|
+
await writeMarker(plan, repo, ref, runtime);
|
|
918
|
+
consola.success(`Updated ${skillName} for ${plan.label}: ${plan.skillDir}`);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
await cloneSkill(plan, repo, ref, runtime);
|
|
922
|
+
await writeMarker(plan, repo, ref, runtime);
|
|
923
|
+
consola.success(`Installed ${skillName} for ${plan.label}: ${plan.skillDir}`);
|
|
924
|
+
};
|
|
925
|
+
const installAgentSkill = async (args, runtime = defaultRuntime) => {
|
|
926
|
+
const repo = args.repo ?? defaultRepo;
|
|
927
|
+
const ref = args.ref ?? defaultRef;
|
|
928
|
+
const plans = createInstallPlans(args);
|
|
929
|
+
consola.info(`Installing community ${skillName} skill from ${repo} (${ref})`);
|
|
930
|
+
for (const plan of plans) {
|
|
931
|
+
await installOne(plan, args, runtime);
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
const installArgs = {
|
|
935
|
+
client: {
|
|
936
|
+
type: "enum",
|
|
937
|
+
description: "Client target: shared, codex, cursor, copilot, claude, cline, all",
|
|
938
|
+
options: [...clientValues]
|
|
939
|
+
},
|
|
940
|
+
codex: {
|
|
941
|
+
type: "boolean",
|
|
942
|
+
description: "Install into the shared ~/.agents/skills path used by Codex"
|
|
943
|
+
},
|
|
944
|
+
cursor: {
|
|
945
|
+
type: "boolean",
|
|
946
|
+
description: "Install into the shared ~/.agents/skills path used by Cursor"
|
|
947
|
+
},
|
|
948
|
+
copilot: {
|
|
949
|
+
type: "boolean",
|
|
950
|
+
description: "Install into the shared ~/.agents/skills path used by GitHub Copilot"
|
|
951
|
+
},
|
|
952
|
+
claude: {
|
|
953
|
+
type: "boolean",
|
|
954
|
+
description: "Install into ~/.claude/skills for Claude Code"
|
|
955
|
+
},
|
|
956
|
+
cline: {
|
|
957
|
+
type: "boolean",
|
|
958
|
+
description: "Install into ~/.cline/skills for Cline"
|
|
959
|
+
},
|
|
960
|
+
all: {
|
|
961
|
+
type: "boolean",
|
|
962
|
+
description: "Install shared, Claude Code, and Cline targets"
|
|
963
|
+
},
|
|
964
|
+
target: {
|
|
965
|
+
type: "string",
|
|
966
|
+
description: "Custom skills root or full skill directory"
|
|
967
|
+
},
|
|
968
|
+
repo: {
|
|
969
|
+
type: "string",
|
|
970
|
+
description: "Skill repository URL",
|
|
971
|
+
default: defaultRepo
|
|
972
|
+
},
|
|
973
|
+
ref: {
|
|
974
|
+
type: "string",
|
|
975
|
+
description: "Git branch or tag to install",
|
|
976
|
+
default: defaultRef
|
|
977
|
+
},
|
|
978
|
+
force: {
|
|
979
|
+
type: "boolean",
|
|
980
|
+
description: "Overwrite an existing unmanaged skill directory"
|
|
981
|
+
},
|
|
982
|
+
"dry-run": {
|
|
983
|
+
type: "boolean",
|
|
984
|
+
description: "Show target directories without writing files"
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
const install = defineCommand({
|
|
988
|
+
meta: {
|
|
989
|
+
description: "Install the community aact-architect skill for AI agents"
|
|
990
|
+
},
|
|
991
|
+
args: installArgs,
|
|
992
|
+
async run({ args }) {
|
|
993
|
+
await installAgentSkill(args);
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
const skill = defineCommand({
|
|
997
|
+
meta: {
|
|
998
|
+
description: "Install agent skills for aact workflows"
|
|
999
|
+
},
|
|
1000
|
+
args: installArgs,
|
|
1001
|
+
default: "install",
|
|
1002
|
+
subCommands: { install }
|
|
1003
|
+
});
|
|
1004
|
+
|
|
711
1005
|
const main = defineCommand({
|
|
712
1006
|
meta: {
|
|
713
1007
|
name: "aact",
|
|
714
1008
|
version,
|
|
715
1009
|
description: "Architecture analysis and compliance tool"
|
|
716
1010
|
},
|
|
717
|
-
subCommands: { init, check, analyze, generate, rule }
|
|
1011
|
+
subCommands: { init, check, analyze, generate, rule, skill }
|
|
718
1012
|
});
|
|
719
1013
|
void runMain(main);
|