@agiflowai/aicode-toolkit 1.0.20 → 1.0.22
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 +83 -0
- package/dist/cli.cjs +186 -13
- package/dist/cli.mjs +188 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -56,6 +56,89 @@ This will:
|
|
|
56
56
|
| `--path <path>` | Custom templates path | `./templates` |
|
|
57
57
|
| `--no-download` | Skip template download | `false` |
|
|
58
58
|
|
|
59
|
+
### `sync`
|
|
60
|
+
|
|
61
|
+
Generate platform config files from `.toolkit/settings.yaml` as the single source of truth.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Generate both .claude/settings.json and mcp-config.yaml
|
|
65
|
+
npx @agiflowai/aicode-toolkit sync
|
|
66
|
+
|
|
67
|
+
# Generate only .claude/settings.json (Claude Code hooks)
|
|
68
|
+
npx @agiflowai/aicode-toolkit sync --hooks
|
|
69
|
+
|
|
70
|
+
# Generate only mcp-config.yaml (MCP server list)
|
|
71
|
+
npx @agiflowai/aicode-toolkit sync --mcp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Options:**
|
|
75
|
+
| Option | Description |
|
|
76
|
+
|--------|-------------|
|
|
77
|
+
| `--hooks` | Write `.claude/settings.json` only |
|
|
78
|
+
| `--mcp` | Write `mcp-config.yaml` only |
|
|
79
|
+
|
|
80
|
+
#### Hooks → `.claude/settings.json`
|
|
81
|
+
|
|
82
|
+
Hook commands are derived automatically from `mcp-config.servers` by replacing
|
|
83
|
+
`mcp-serve` with `hook --type claude-code.<method>`. Configure which methods to
|
|
84
|
+
activate in `.toolkit/settings.yaml`:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
scaffold-mcp:
|
|
88
|
+
hook:
|
|
89
|
+
claude-code:
|
|
90
|
+
preToolUse:
|
|
91
|
+
args: # extra CLI args appended to the generated hook command
|
|
92
|
+
llm-tool: gemini-cli
|
|
93
|
+
postToolUse: {}
|
|
94
|
+
stop: {}
|
|
95
|
+
userPromptSubmit: {}
|
|
96
|
+
taskCompleted: {}
|
|
97
|
+
|
|
98
|
+
architect-mcp:
|
|
99
|
+
hook:
|
|
100
|
+
claude-code:
|
|
101
|
+
preToolUse:
|
|
102
|
+
args:
|
|
103
|
+
llm-tool: gemini-cli
|
|
104
|
+
postToolUse: {}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Generated hook entries fire on all tool calls (no matcher). Run `aicode sync --hooks`
|
|
108
|
+
to write `.claude/settings.json`.
|
|
109
|
+
|
|
110
|
+
#### `mcp-config` section → `mcp-config.yaml`
|
|
111
|
+
|
|
112
|
+
Define MCP servers in `.toolkit/settings.yaml`:
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
mcp-config:
|
|
116
|
+
servers:
|
|
117
|
+
scaffold-mcp:
|
|
118
|
+
command: bun
|
|
119
|
+
args:
|
|
120
|
+
- run
|
|
121
|
+
- packages/scaffold-mcp/src/cli.ts
|
|
122
|
+
- mcp-serve
|
|
123
|
+
- --admin-enable
|
|
124
|
+
- --prompt-as-skill
|
|
125
|
+
instruction: "Use this server for generating boilerplate code and scaffolding."
|
|
126
|
+
architect-mcp:
|
|
127
|
+
command: bun
|
|
128
|
+
args:
|
|
129
|
+
- run
|
|
130
|
+
- packages/architect-mcp/src/cli.ts
|
|
131
|
+
- mcp-serve
|
|
132
|
+
instruction: "Use this server for design pattern guidance and code review."
|
|
133
|
+
skills:
|
|
134
|
+
paths:
|
|
135
|
+
- docs/skills
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Run `aicode sync --mcp` to write `mcp-config.yaml` from this config.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
59
142
|
### `add`
|
|
60
143
|
|
|
61
144
|
Add templates from GitHub repositories.
|
package/dist/cli.cjs
CHANGED
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
const require_utils = require('./utils-r_182Nor.cjs');
|
|
3
3
|
const require_mcp = require('./mcp--A-5zuBI.cjs');
|
|
4
4
|
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
5
|
+
let node_fs_promises = require("node:fs/promises");
|
|
5
6
|
let node_path = require("node:path");
|
|
6
7
|
node_path = require_utils.__toESM(node_path);
|
|
8
|
+
let liquidjs = require("liquidjs");
|
|
7
9
|
let commander = require("commander");
|
|
8
10
|
let __inquirer_prompts = require("@inquirer/prompts");
|
|
9
11
|
let ora = require("ora");
|
|
10
12
|
ora = require_utils.__toESM(ora);
|
|
11
13
|
let xstate = require("xstate");
|
|
14
|
+
let js_yaml = require("js-yaml");
|
|
15
|
+
js_yaml = require_utils.__toESM(js_yaml);
|
|
12
16
|
|
|
13
17
|
//#region package.json
|
|
14
|
-
var version = "1.0.
|
|
18
|
+
var version = "1.0.21";
|
|
15
19
|
|
|
16
20
|
//#endregion
|
|
17
21
|
//#region src/commands/add.ts
|
|
@@ -75,6 +79,10 @@ const addCommand = new commander.Command("add").description("Add a template to t
|
|
|
75
79
|
}
|
|
76
80
|
});
|
|
77
81
|
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/templates/settings.yaml.liquid?raw
|
|
84
|
+
var settings_yaml_default = "# .toolkit/settings.yaml\n# Toolkit configuration for aicode-toolkit\n#\n# Local overrides go in .toolkit/settings.local.yaml (add to .gitignore).\n# Local settings are shallow-merged over this file — useful for per-developer\n# LLM tool preferences or API keys without committing them.\n\n# ---------------------------------------------------------------------------\n# Project identity\n# ---------------------------------------------------------------------------\n{% if projectType %}\nprojectType: {{ projectType }}\n{% else %}\n# projectType: monolith\n{% endif %}\n{% if templatesPath %}\ntemplatesPath: {{ templatesPath }}\n{% else %}\n# templatesPath: ./templates\n{% endif %}\n{% if sourceTemplate %}\nsourceTemplate: {{ sourceTemplate }}\n{% else %}\n# sourceTemplate: react-vite\n{% endif %}\nversion: '1.0'\n\n# ---------------------------------------------------------------------------\n# scaffold-mcp — configuration for the scaffold-mcp CLI\n# CLI flags always take precedence over values set here.\n# ---------------------------------------------------------------------------\n\nscaffold-mcp:\n\n # -------------------------------------------------------------------------\n # mcp-serve — defaults for `scaffold-mcp mcp-serve`\n # -------------------------------------------------------------------------\n mcp-serve:\n # type: stdio\n # port: 3000\n # host: localhost\n # adminEnable: false\n # promptAsSkill: false\n {% if fallbackTool %}\n fallbackTool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n fallbackToolConfig:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% else %}\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n {% else %}\n # fallbackTool: gemini-cli\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n # Extra CLI args merged into the mcp-serve command (key: value → --key value)\n # args:\n # review-tool: gemini-cli\n\n # -------------------------------------------------------------------------\n # hook — defaults for `scaffold-mcp hook`\n # Keyed by agent name, then by hook method.\n # Supported agents : claude-code | gemini-cli\n # Supported methods : preToolUse | postToolUse | userPromptSubmit | taskCompleted | stop\n # -------------------------------------------------------------------------\n hook:\n\n claude-code:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n gemini-cli:\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n# ---------------------------------------------------------------------------\n# architect-mcp — configuration for the architect-mcp CLI\n# CLI flags always take precedence over values set here.\n# ---------------------------------------------------------------------------\n\narchitect-mcp:\n\n # -------------------------------------------------------------------------\n # mcp-serve — defaults for `architect-mcp mcp-serve`\n # -------------------------------------------------------------------------\n mcp-serve:\n # type: stdio\n # port: 3000\n # host: localhost\n # adminEnable: false\n {% if fallbackTool %}\n fallbackTool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n fallbackToolConfig:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% else %}\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n {% else %}\n # fallbackTool: gemini-cli\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n # designPatternTool: gemini-cli\n # designPatternToolConfig:\n # model: gemini-2.0-flash\n # reviewTool: gemini-cli\n # reviewToolConfig:\n # model: gemini-2.0-flash\n # Extra CLI args merged into the mcp-serve command (key: value → --key value)\n # args:\n # review-tool: gemini-cli\n\n # -------------------------------------------------------------------------\n # hook — defaults for `architect-mcp hook`\n # Keyed by agent name, then by hook method.\n # Supported agents : claude-code | gemini-cli\n # Supported methods : preToolUse | postToolUse\n # -------------------------------------------------------------------------\n hook:\n\n claude-code:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n gemini-cli:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n# ---------------------------------------------------------------------------\n# mcp-config — generates mcp-config.yaml via `aicode sync --mcp`\n# Servers listed here become entries in mcp-config.yaml.\n# Run `aicode sync` to write the output file.\n# ---------------------------------------------------------------------------\n\n# mcp-config:\n# servers:\n# scaffold-mcp:\n# command: bun\n# args:\n# - run\n# - packages/scaffold-mcp/src/cli.ts\n# - mcp-serve\n# - --admin-enable\n# - --prompt-as-skill\n# instruction: \"Use this server for generating boilerplate code, scaffolding new projects, and adding features to existing projects.\"\n# architect-mcp:\n# command: bun\n# args:\n# - run\n# - packages/architect-mcp/src/cli.ts\n# - mcp-serve\n# instruction: \"Use this server for design pattern guidance and code review.\"\n# skills:\n# paths:\n# - docs/skills\n";
|
|
85
|
+
|
|
78
86
|
//#endregion
|
|
79
87
|
//#region src/states/init-machine.ts
|
|
80
88
|
/**
|
|
@@ -721,15 +729,21 @@ const initActors = {
|
|
|
721
729
|
}),
|
|
722
730
|
createConfig: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
723
731
|
if (actorInput.projectType === __agiflowai_aicode_utils.ProjectType.MONOLITH) {
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
templatesPath: node_path.default.relative(actorInput.workspaceRoot, actorInput.templatesPath) || "templates",
|
|
732
|
+
const relativeTemplatesPath = node_path.default.relative(actorInput.workspaceRoot, actorInput.templatesPath);
|
|
733
|
+
const content = await new liquidjs.Liquid().parseAndRender(settings_yaml_default, {
|
|
727
734
|
projectType: "monolith",
|
|
735
|
+
templatesPath: relativeTemplatesPath || "templates",
|
|
728
736
|
sourceTemplate: actorInput.selectedTemplates[0]
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
__agiflowai_aicode_utils.print.
|
|
737
|
+
});
|
|
738
|
+
const toolkitDir = node_path.default.join(actorInput.workspaceRoot, ".toolkit");
|
|
739
|
+
const settingsPath = node_path.default.join(toolkitDir, "settings.yaml");
|
|
740
|
+
__agiflowai_aicode_utils.print.info("\nCreating .toolkit/settings.yaml...");
|
|
741
|
+
await (0, node_fs_promises.mkdir)(toolkitDir, { recursive: true });
|
|
742
|
+
await (0, node_fs_promises.writeFile)(settingsPath, content, {
|
|
743
|
+
encoding: "utf-8",
|
|
744
|
+
flag: "w"
|
|
745
|
+
});
|
|
746
|
+
__agiflowai_aicode_utils.print.success(".toolkit/settings.yaml created");
|
|
733
747
|
}
|
|
734
748
|
}),
|
|
735
749
|
detectCodingAgent: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
@@ -897,17 +911,176 @@ const initCommand = new commander.Command("init").description("Initialize projec
|
|
|
897
911
|
}
|
|
898
912
|
});
|
|
899
913
|
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/commands/sync.ts
|
|
916
|
+
/**
|
|
917
|
+
* Sync Command
|
|
918
|
+
*
|
|
919
|
+
* DESIGN PATTERNS:
|
|
920
|
+
* - Command pattern with Commander for CLI argument parsing
|
|
921
|
+
* - Async/await pattern for asynchronous operations
|
|
922
|
+
* - Error handling pattern with try-catch and proper exit codes
|
|
923
|
+
*
|
|
924
|
+
* CODING STANDARDS:
|
|
925
|
+
* - Use async action handlers for asynchronous operations
|
|
926
|
+
* - Provide clear option descriptions and default values
|
|
927
|
+
* - Handle errors gracefully with process.exit()
|
|
928
|
+
* - Log progress and errors to console
|
|
929
|
+
* - Use Commander's .option() and .argument() for inputs
|
|
930
|
+
*
|
|
931
|
+
* AVOID:
|
|
932
|
+
* - Synchronous blocking operations in action handlers
|
|
933
|
+
* - Missing error handling (always use try-catch)
|
|
934
|
+
* - Hardcoded values (use options or environment variables)
|
|
935
|
+
* - Not exiting with appropriate exit codes on errors
|
|
936
|
+
*/
|
|
937
|
+
const CLAUDE_SETTINGS_DIR = ".claude";
|
|
938
|
+
const CLAUDE_SETTINGS_FILE = "settings.json";
|
|
939
|
+
const MCP_CONFIG_FILE = "mcp-config.yaml";
|
|
940
|
+
function hasHookConfig(config) {
|
|
941
|
+
return !!(config["scaffold-mcp"]?.hook?.["claude-code"] || config["architect-mcp"]?.hook?.["claude-code"]);
|
|
942
|
+
}
|
|
943
|
+
function argsToFlags(args) {
|
|
944
|
+
const flags = [];
|
|
945
|
+
for (const [key, value] of Object.entries(args)) {
|
|
946
|
+
if (value === false) continue;
|
|
947
|
+
flags.push(`--${key}`);
|
|
948
|
+
if (value !== true) flags.push(String(value));
|
|
949
|
+
}
|
|
950
|
+
return flags;
|
|
951
|
+
}
|
|
952
|
+
function buildHookCommand(server, hookType, extraFlags) {
|
|
953
|
+
const serverArgs = server.args ?? [];
|
|
954
|
+
const scriptIdx = serverArgs.findIndex((arg) => /\.(ts|js|mjs|cjs)$/.test(arg));
|
|
955
|
+
if (scriptIdx >= 0) {
|
|
956
|
+
const prefixArgs$1 = serverArgs.slice(0, scriptIdx + 1);
|
|
957
|
+
return [
|
|
958
|
+
server.command,
|
|
959
|
+
...prefixArgs$1,
|
|
960
|
+
"hook",
|
|
961
|
+
"--type",
|
|
962
|
+
hookType,
|
|
963
|
+
...extraFlags
|
|
964
|
+
].join(" ");
|
|
965
|
+
}
|
|
966
|
+
const mcpServeIdx = serverArgs.indexOf("mcp-serve");
|
|
967
|
+
const prefixArgs = mcpServeIdx >= 0 ? serverArgs.slice(0, mcpServeIdx) : serverArgs;
|
|
968
|
+
return [
|
|
969
|
+
server.command,
|
|
970
|
+
...prefixArgs,
|
|
971
|
+
"hook",
|
|
972
|
+
"--type",
|
|
973
|
+
hookType,
|
|
974
|
+
...extraFlags
|
|
975
|
+
].join(" ");
|
|
976
|
+
}
|
|
977
|
+
function addHookEntry(output, event, command) {
|
|
978
|
+
if (!output[event]) output[event] = [];
|
|
979
|
+
output[event].push({ hooks: [{
|
|
980
|
+
type: "command",
|
|
981
|
+
command
|
|
982
|
+
}] });
|
|
983
|
+
}
|
|
984
|
+
function isMcpConfigYaml(value) {
|
|
985
|
+
return typeof value === "object" && value !== null;
|
|
986
|
+
}
|
|
987
|
+
async function readMcpConfigYaml(workspaceRoot) {
|
|
988
|
+
try {
|
|
989
|
+
const content = await (0, node_fs_promises.readFile)(node_path.default.join(workspaceRoot, MCP_CONFIG_FILE), "utf-8");
|
|
990
|
+
const parsed = js_yaml.default.load(content);
|
|
991
|
+
if (!isMcpConfigYaml(parsed)) throw new Error(`${MCP_CONFIG_FILE} has unexpected structure`);
|
|
992
|
+
return parsed;
|
|
993
|
+
} catch (err) {
|
|
994
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") return {};
|
|
995
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
996
|
+
throw new Error(`Failed to read ${MCP_CONFIG_FILE}: ${message}`);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const METHOD_TO_EVENT = {
|
|
1000
|
+
preToolUse: "PreToolUse",
|
|
1001
|
+
postToolUse: "PostToolUse",
|
|
1002
|
+
stop: "Stop",
|
|
1003
|
+
userPromptSubmit: "UserPromptSubmit",
|
|
1004
|
+
taskCompleted: "TaskCompleted"
|
|
1005
|
+
};
|
|
1006
|
+
async function writeClaudeSettings(config, workspaceRoot) {
|
|
1007
|
+
try {
|
|
1008
|
+
const mcpServers = (await readMcpConfigYaml(workspaceRoot)).mcpServers ?? {};
|
|
1009
|
+
const hooksOutput = {};
|
|
1010
|
+
let hasAny = false;
|
|
1011
|
+
const scaffoldAgent = config["scaffold-mcp"]?.hook?.["claude-code"];
|
|
1012
|
+
const scaffoldServer = mcpServers["scaffold-mcp"];
|
|
1013
|
+
if (scaffoldAgent && scaffoldServer) for (const [method, methodConfig] of [
|
|
1014
|
+
["preToolUse", scaffoldAgent.preToolUse],
|
|
1015
|
+
["postToolUse", scaffoldAgent.postToolUse],
|
|
1016
|
+
["stop", scaffoldAgent.stop],
|
|
1017
|
+
["userPromptSubmit", scaffoldAgent.userPromptSubmit],
|
|
1018
|
+
["taskCompleted", scaffoldAgent.taskCompleted]
|
|
1019
|
+
]) {
|
|
1020
|
+
if (methodConfig === void 0) continue;
|
|
1021
|
+
const extraFlags = methodConfig?.args ? argsToFlags(methodConfig?.args) : [];
|
|
1022
|
+
const command = buildHookCommand(scaffoldServer, `claude-code.${method}`, extraFlags);
|
|
1023
|
+
addHookEntry(hooksOutput, METHOD_TO_EVENT[method], command);
|
|
1024
|
+
hasAny = true;
|
|
1025
|
+
}
|
|
1026
|
+
const architectAgent = config["architect-mcp"]?.hook?.["claude-code"];
|
|
1027
|
+
const architectServer = mcpServers["architect-mcp"];
|
|
1028
|
+
if (architectAgent && architectServer) for (const [method, methodConfig] of [["preToolUse", architectAgent.preToolUse], ["postToolUse", architectAgent.postToolUse]]) {
|
|
1029
|
+
if (methodConfig === void 0) continue;
|
|
1030
|
+
const extraFlags = methodConfig?.args ? argsToFlags(methodConfig?.args) : [];
|
|
1031
|
+
const command = buildHookCommand(architectServer, `claude-code.${method}`, extraFlags);
|
|
1032
|
+
addHookEntry(hooksOutput, METHOD_TO_EVENT[method], command);
|
|
1033
|
+
hasAny = true;
|
|
1034
|
+
}
|
|
1035
|
+
if (!hasAny) {
|
|
1036
|
+
__agiflowai_aicode_utils.print.warning("No scaffold-mcp/architect-mcp hook.claude-code config found — skipping .claude/settings.json");
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const settings = { hooks: hooksOutput };
|
|
1040
|
+
const claudeDir = node_path.default.join(workspaceRoot, CLAUDE_SETTINGS_DIR);
|
|
1041
|
+
await (0, node_fs_promises.mkdir)(claudeDir, { recursive: true });
|
|
1042
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(claudeDir, CLAUDE_SETTINGS_FILE), JSON.stringify(settings, null, 2), "utf-8");
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1045
|
+
throw new Error(`Failed to write ${CLAUDE_SETTINGS_DIR}/${CLAUDE_SETTINGS_FILE}: ${message}`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Generate .claude/settings.json from .toolkit/settings.yaml and mcp-config.yaml
|
|
1050
|
+
*/
|
|
1051
|
+
const syncCommand = new commander.Command("sync").description("Generate .claude/settings.json from .toolkit/settings.yaml and mcp-config.yaml").action(async () => {
|
|
1052
|
+
try {
|
|
1053
|
+
const [workspaceRoot, config] = await Promise.all([__agiflowai_aicode_utils.TemplatesManagerService.getWorkspaceRoot(), __agiflowai_aicode_utils.TemplatesManagerService.readToolkitConfig()]);
|
|
1054
|
+
if (!config) throw new Error("No .toolkit/settings.yaml found. Run `aicode init` first.");
|
|
1055
|
+
if (hasHookConfig(config)) {
|
|
1056
|
+
await writeClaudeSettings(config, workspaceRoot);
|
|
1057
|
+
__agiflowai_aicode_utils.print.success("Written .claude/settings.json");
|
|
1058
|
+
} else __agiflowai_aicode_utils.print.warning("No hook.claude-code config found in settings.yaml — skipping");
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1061
|
+
__agiflowai_aicode_utils.print.error(`sync failed: ${message}`);
|
|
1062
|
+
process.exit(1);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
|
|
900
1066
|
//#endregion
|
|
901
1067
|
//#region src/cli.ts
|
|
902
1068
|
/**
|
|
903
1069
|
* Main entry point
|
|
904
1070
|
*/
|
|
905
1071
|
async function main() {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1072
|
+
try {
|
|
1073
|
+
const program = new commander.Command();
|
|
1074
|
+
program.name("aicode").description("AI-powered code toolkit CLI for scaffolding, architecture management, and development workflows").version(version);
|
|
1075
|
+
program.addCommand(initCommand);
|
|
1076
|
+
program.addCommand(addCommand);
|
|
1077
|
+
program.addCommand(syncCommand);
|
|
1078
|
+
await program.parseAsync(process.argv);
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1081
|
+
console.error(`aicode: ${message}`);
|
|
1082
|
+
process.exit(1);
|
|
1083
|
+
}
|
|
911
1084
|
}
|
|
912
1085
|
main();
|
|
913
1086
|
|
package/dist/cli.mjs
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
import { r as MCP_SERVER_INFO, t as MCPServer } from "./mcp-CBcPdzNG.mjs";
|
|
3
3
|
import { a as SPEC_TOOL_INFO, c as NewProjectService, i as TemplateSelectionService, l as CodingAgentService, o as SpecTool, s as SpecToolService, t as displayBanner } from "./utils-QWjfWNVp.mjs";
|
|
4
4
|
import { ProjectType, TemplatesManagerService, cloneRepository, cloneSubdirectory, detectProjectType, ensureDir, findWorkspaceRoot, icons, messages, parseGitHubUrl, pathExists, print, sections } from "@agiflowai/aicode-utils";
|
|
5
|
+
import { mkdir as mkdir$1, readFile as readFile$1, writeFile as writeFile$1 } from "node:fs/promises";
|
|
5
6
|
import path from "node:path";
|
|
7
|
+
import { Liquid } from "liquidjs";
|
|
6
8
|
import { Command } from "commander";
|
|
7
9
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
8
10
|
import ora from "ora";
|
|
9
11
|
import { assign, createActor, createMachine, fromPromise } from "xstate";
|
|
12
|
+
import yaml from "js-yaml";
|
|
10
13
|
|
|
11
14
|
//#region package.json
|
|
12
|
-
var version = "1.0.
|
|
15
|
+
var version = "1.0.21";
|
|
13
16
|
|
|
14
17
|
//#endregion
|
|
15
18
|
//#region src/commands/add.ts
|
|
@@ -73,6 +76,10 @@ const addCommand = new Command("add").description("Add a template to templates f
|
|
|
73
76
|
}
|
|
74
77
|
});
|
|
75
78
|
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/templates/settings.yaml.liquid?raw
|
|
81
|
+
var settings_yaml_default = "# .toolkit/settings.yaml\n# Toolkit configuration for aicode-toolkit\n#\n# Local overrides go in .toolkit/settings.local.yaml (add to .gitignore).\n# Local settings are shallow-merged over this file — useful for per-developer\n# LLM tool preferences or API keys without committing them.\n\n# ---------------------------------------------------------------------------\n# Project identity\n# ---------------------------------------------------------------------------\n{% if projectType %}\nprojectType: {{ projectType }}\n{% else %}\n# projectType: monolith\n{% endif %}\n{% if templatesPath %}\ntemplatesPath: {{ templatesPath }}\n{% else %}\n# templatesPath: ./templates\n{% endif %}\n{% if sourceTemplate %}\nsourceTemplate: {{ sourceTemplate }}\n{% else %}\n# sourceTemplate: react-vite\n{% endif %}\nversion: '1.0'\n\n# ---------------------------------------------------------------------------\n# scaffold-mcp — configuration for the scaffold-mcp CLI\n# CLI flags always take precedence over values set here.\n# ---------------------------------------------------------------------------\n\nscaffold-mcp:\n\n # -------------------------------------------------------------------------\n # mcp-serve — defaults for `scaffold-mcp mcp-serve`\n # -------------------------------------------------------------------------\n mcp-serve:\n # type: stdio\n # port: 3000\n # host: localhost\n # adminEnable: false\n # promptAsSkill: false\n {% if fallbackTool %}\n fallbackTool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n fallbackToolConfig:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% else %}\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n {% else %}\n # fallbackTool: gemini-cli\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n # Extra CLI args merged into the mcp-serve command (key: value → --key value)\n # args:\n # review-tool: gemini-cli\n\n # -------------------------------------------------------------------------\n # hook — defaults for `scaffold-mcp hook`\n # Keyed by agent name, then by hook method.\n # Supported agents : claude-code | gemini-cli\n # Supported methods : preToolUse | postToolUse | userPromptSubmit | taskCompleted | stop\n # -------------------------------------------------------------------------\n hook:\n\n claude-code:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n gemini-cli:\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n# ---------------------------------------------------------------------------\n# architect-mcp — configuration for the architect-mcp CLI\n# CLI flags always take precedence over values set here.\n# ---------------------------------------------------------------------------\n\narchitect-mcp:\n\n # -------------------------------------------------------------------------\n # mcp-serve — defaults for `architect-mcp mcp-serve`\n # -------------------------------------------------------------------------\n mcp-serve:\n # type: stdio\n # port: 3000\n # host: localhost\n # adminEnable: false\n {% if fallbackTool %}\n fallbackTool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n fallbackToolConfig:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% else %}\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n {% else %}\n # fallbackTool: gemini-cli\n # fallbackToolConfig:\n # model: gemini-2.0-flash\n {% endif %}\n # designPatternTool: gemini-cli\n # designPatternToolConfig:\n # model: gemini-2.0-flash\n # reviewTool: gemini-cli\n # reviewToolConfig:\n # model: gemini-2.0-flash\n # Extra CLI args merged into the mcp-serve command (key: value → --key value)\n # args:\n # review-tool: gemini-cli\n\n # -------------------------------------------------------------------------\n # hook — defaults for `architect-mcp hook`\n # Keyed by agent name, then by hook method.\n # Supported agents : claude-code | gemini-cli\n # Supported methods : preToolUse | postToolUse\n # -------------------------------------------------------------------------\n hook:\n\n claude-code:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n # args:\n # llm-tool: gemini-cli\n\n gemini-cli:\n preToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n postToolUse:\n {% if fallbackTool %}\n llm-tool: {{ fallbackTool }}\n {% if fallbackToolConfig %}\n tool-config:\n {% for pair in fallbackToolConfig %}\n {{ pair[0] }}: {{ pair[1] }}\n {% endfor %}\n {% endif %}\n {% else %}\n # llm-tool: gemini-cli\n # tool-config:\n # model: gemini-2.0-flash\n {% endif %}\n\n# ---------------------------------------------------------------------------\n# mcp-config — generates mcp-config.yaml via `aicode sync --mcp`\n# Servers listed here become entries in mcp-config.yaml.\n# Run `aicode sync` to write the output file.\n# ---------------------------------------------------------------------------\n\n# mcp-config:\n# servers:\n# scaffold-mcp:\n# command: bun\n# args:\n# - run\n# - packages/scaffold-mcp/src/cli.ts\n# - mcp-serve\n# - --admin-enable\n# - --prompt-as-skill\n# instruction: \"Use this server for generating boilerplate code, scaffolding new projects, and adding features to existing projects.\"\n# architect-mcp:\n# command: bun\n# args:\n# - run\n# - packages/architect-mcp/src/cli.ts\n# - mcp-serve\n# instruction: \"Use this server for design pattern guidance and code review.\"\n# skills:\n# paths:\n# - docs/skills\n";
|
|
82
|
+
|
|
76
83
|
//#endregion
|
|
77
84
|
//#region src/states/init-machine.ts
|
|
78
85
|
/**
|
|
@@ -598,11 +605,11 @@ const initActors = {
|
|
|
598
605
|
}),
|
|
599
606
|
checkTemplatesFolder: fromPromise(async ({ input: actorInput }) => {
|
|
600
607
|
try {
|
|
601
|
-
const fs = await import("node:fs/promises");
|
|
608
|
+
const fs$1 = await import("node:fs/promises");
|
|
602
609
|
const defaultTemplatesPath = path.join(actorInput.workspaceRoot, "templates");
|
|
603
610
|
let templatesExists = false;
|
|
604
611
|
try {
|
|
605
|
-
await fs.access(defaultTemplatesPath);
|
|
612
|
+
await fs$1.access(defaultTemplatesPath);
|
|
606
613
|
templatesExists = true;
|
|
607
614
|
} catch {
|
|
608
615
|
templatesExists = false;
|
|
@@ -630,7 +637,7 @@ const initActors = {
|
|
|
630
637
|
print.info("");
|
|
631
638
|
finalTemplatesPath = path.join(actorInput.workspaceRoot, customDir.trim());
|
|
632
639
|
try {
|
|
633
|
-
await fs.mkdir(finalTemplatesPath, { recursive: true });
|
|
640
|
+
await fs$1.mkdir(finalTemplatesPath, { recursive: true });
|
|
634
641
|
print.success(`Created templates directory at: ${finalTemplatesPath}`);
|
|
635
642
|
} catch (error) {
|
|
636
643
|
throw new Error(`Failed to create templates directory at ${finalTemplatesPath}: ${error.message}`);
|
|
@@ -719,15 +726,21 @@ const initActors = {
|
|
|
719
726
|
}),
|
|
720
727
|
createConfig: fromPromise(async ({ input: actorInput }) => {
|
|
721
728
|
if (actorInput.projectType === ProjectType.MONOLITH) {
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
templatesPath: path.relative(actorInput.workspaceRoot, actorInput.templatesPath) || "templates",
|
|
729
|
+
const relativeTemplatesPath = path.relative(actorInput.workspaceRoot, actorInput.templatesPath);
|
|
730
|
+
const content = await new Liquid().parseAndRender(settings_yaml_default, {
|
|
725
731
|
projectType: "monolith",
|
|
732
|
+
templatesPath: relativeTemplatesPath || "templates",
|
|
726
733
|
sourceTemplate: actorInput.selectedTemplates[0]
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
print.
|
|
734
|
+
});
|
|
735
|
+
const toolkitDir = path.join(actorInput.workspaceRoot, ".toolkit");
|
|
736
|
+
const settingsPath = path.join(toolkitDir, "settings.yaml");
|
|
737
|
+
print.info("\nCreating .toolkit/settings.yaml...");
|
|
738
|
+
await mkdir$1(toolkitDir, { recursive: true });
|
|
739
|
+
await writeFile$1(settingsPath, content, {
|
|
740
|
+
encoding: "utf-8",
|
|
741
|
+
flag: "w"
|
|
742
|
+
});
|
|
743
|
+
print.success(".toolkit/settings.yaml created");
|
|
731
744
|
}
|
|
732
745
|
}),
|
|
733
746
|
detectCodingAgent: fromPromise(async ({ input: actorInput }) => {
|
|
@@ -895,17 +908,176 @@ const initCommand = new Command("init").description("Initialize project with tem
|
|
|
895
908
|
}
|
|
896
909
|
});
|
|
897
910
|
|
|
911
|
+
//#endregion
|
|
912
|
+
//#region src/commands/sync.ts
|
|
913
|
+
/**
|
|
914
|
+
* Sync Command
|
|
915
|
+
*
|
|
916
|
+
* DESIGN PATTERNS:
|
|
917
|
+
* - Command pattern with Commander for CLI argument parsing
|
|
918
|
+
* - Async/await pattern for asynchronous operations
|
|
919
|
+
* - Error handling pattern with try-catch and proper exit codes
|
|
920
|
+
*
|
|
921
|
+
* CODING STANDARDS:
|
|
922
|
+
* - Use async action handlers for asynchronous operations
|
|
923
|
+
* - Provide clear option descriptions and default values
|
|
924
|
+
* - Handle errors gracefully with process.exit()
|
|
925
|
+
* - Log progress and errors to console
|
|
926
|
+
* - Use Commander's .option() and .argument() for inputs
|
|
927
|
+
*
|
|
928
|
+
* AVOID:
|
|
929
|
+
* - Synchronous blocking operations in action handlers
|
|
930
|
+
* - Missing error handling (always use try-catch)
|
|
931
|
+
* - Hardcoded values (use options or environment variables)
|
|
932
|
+
* - Not exiting with appropriate exit codes on errors
|
|
933
|
+
*/
|
|
934
|
+
const CLAUDE_SETTINGS_DIR = ".claude";
|
|
935
|
+
const CLAUDE_SETTINGS_FILE = "settings.json";
|
|
936
|
+
const MCP_CONFIG_FILE = "mcp-config.yaml";
|
|
937
|
+
function hasHookConfig(config) {
|
|
938
|
+
return !!(config["scaffold-mcp"]?.hook?.["claude-code"] || config["architect-mcp"]?.hook?.["claude-code"]);
|
|
939
|
+
}
|
|
940
|
+
function argsToFlags(args) {
|
|
941
|
+
const flags = [];
|
|
942
|
+
for (const [key, value] of Object.entries(args)) {
|
|
943
|
+
if (value === false) continue;
|
|
944
|
+
flags.push(`--${key}`);
|
|
945
|
+
if (value !== true) flags.push(String(value));
|
|
946
|
+
}
|
|
947
|
+
return flags;
|
|
948
|
+
}
|
|
949
|
+
function buildHookCommand(server, hookType, extraFlags) {
|
|
950
|
+
const serverArgs = server.args ?? [];
|
|
951
|
+
const scriptIdx = serverArgs.findIndex((arg) => /\.(ts|js|mjs|cjs)$/.test(arg));
|
|
952
|
+
if (scriptIdx >= 0) {
|
|
953
|
+
const prefixArgs$1 = serverArgs.slice(0, scriptIdx + 1);
|
|
954
|
+
return [
|
|
955
|
+
server.command,
|
|
956
|
+
...prefixArgs$1,
|
|
957
|
+
"hook",
|
|
958
|
+
"--type",
|
|
959
|
+
hookType,
|
|
960
|
+
...extraFlags
|
|
961
|
+
].join(" ");
|
|
962
|
+
}
|
|
963
|
+
const mcpServeIdx = serverArgs.indexOf("mcp-serve");
|
|
964
|
+
const prefixArgs = mcpServeIdx >= 0 ? serverArgs.slice(0, mcpServeIdx) : serverArgs;
|
|
965
|
+
return [
|
|
966
|
+
server.command,
|
|
967
|
+
...prefixArgs,
|
|
968
|
+
"hook",
|
|
969
|
+
"--type",
|
|
970
|
+
hookType,
|
|
971
|
+
...extraFlags
|
|
972
|
+
].join(" ");
|
|
973
|
+
}
|
|
974
|
+
function addHookEntry(output, event, command) {
|
|
975
|
+
if (!output[event]) output[event] = [];
|
|
976
|
+
output[event].push({ hooks: [{
|
|
977
|
+
type: "command",
|
|
978
|
+
command
|
|
979
|
+
}] });
|
|
980
|
+
}
|
|
981
|
+
function isMcpConfigYaml(value) {
|
|
982
|
+
return typeof value === "object" && value !== null;
|
|
983
|
+
}
|
|
984
|
+
async function readMcpConfigYaml(workspaceRoot) {
|
|
985
|
+
try {
|
|
986
|
+
const content = await readFile$1(path.join(workspaceRoot, MCP_CONFIG_FILE), "utf-8");
|
|
987
|
+
const parsed = yaml.load(content);
|
|
988
|
+
if (!isMcpConfigYaml(parsed)) throw new Error(`${MCP_CONFIG_FILE} has unexpected structure`);
|
|
989
|
+
return parsed;
|
|
990
|
+
} catch (err) {
|
|
991
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") return {};
|
|
992
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
993
|
+
throw new Error(`Failed to read ${MCP_CONFIG_FILE}: ${message}`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const METHOD_TO_EVENT = {
|
|
997
|
+
preToolUse: "PreToolUse",
|
|
998
|
+
postToolUse: "PostToolUse",
|
|
999
|
+
stop: "Stop",
|
|
1000
|
+
userPromptSubmit: "UserPromptSubmit",
|
|
1001
|
+
taskCompleted: "TaskCompleted"
|
|
1002
|
+
};
|
|
1003
|
+
async function writeClaudeSettings(config, workspaceRoot) {
|
|
1004
|
+
try {
|
|
1005
|
+
const mcpServers = (await readMcpConfigYaml(workspaceRoot)).mcpServers ?? {};
|
|
1006
|
+
const hooksOutput = {};
|
|
1007
|
+
let hasAny = false;
|
|
1008
|
+
const scaffoldAgent = config["scaffold-mcp"]?.hook?.["claude-code"];
|
|
1009
|
+
const scaffoldServer = mcpServers["scaffold-mcp"];
|
|
1010
|
+
if (scaffoldAgent && scaffoldServer) for (const [method, methodConfig] of [
|
|
1011
|
+
["preToolUse", scaffoldAgent.preToolUse],
|
|
1012
|
+
["postToolUse", scaffoldAgent.postToolUse],
|
|
1013
|
+
["stop", scaffoldAgent.stop],
|
|
1014
|
+
["userPromptSubmit", scaffoldAgent.userPromptSubmit],
|
|
1015
|
+
["taskCompleted", scaffoldAgent.taskCompleted]
|
|
1016
|
+
]) {
|
|
1017
|
+
if (methodConfig === void 0) continue;
|
|
1018
|
+
const extraFlags = methodConfig?.args ? argsToFlags(methodConfig?.args) : [];
|
|
1019
|
+
const command = buildHookCommand(scaffoldServer, `claude-code.${method}`, extraFlags);
|
|
1020
|
+
addHookEntry(hooksOutput, METHOD_TO_EVENT[method], command);
|
|
1021
|
+
hasAny = true;
|
|
1022
|
+
}
|
|
1023
|
+
const architectAgent = config["architect-mcp"]?.hook?.["claude-code"];
|
|
1024
|
+
const architectServer = mcpServers["architect-mcp"];
|
|
1025
|
+
if (architectAgent && architectServer) for (const [method, methodConfig] of [["preToolUse", architectAgent.preToolUse], ["postToolUse", architectAgent.postToolUse]]) {
|
|
1026
|
+
if (methodConfig === void 0) continue;
|
|
1027
|
+
const extraFlags = methodConfig?.args ? argsToFlags(methodConfig?.args) : [];
|
|
1028
|
+
const command = buildHookCommand(architectServer, `claude-code.${method}`, extraFlags);
|
|
1029
|
+
addHookEntry(hooksOutput, METHOD_TO_EVENT[method], command);
|
|
1030
|
+
hasAny = true;
|
|
1031
|
+
}
|
|
1032
|
+
if (!hasAny) {
|
|
1033
|
+
print.warning("No scaffold-mcp/architect-mcp hook.claude-code config found — skipping .claude/settings.json");
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
const settings = { hooks: hooksOutput };
|
|
1037
|
+
const claudeDir = path.join(workspaceRoot, CLAUDE_SETTINGS_DIR);
|
|
1038
|
+
await mkdir$1(claudeDir, { recursive: true });
|
|
1039
|
+
await writeFile$1(path.join(claudeDir, CLAUDE_SETTINGS_FILE), JSON.stringify(settings, null, 2), "utf-8");
|
|
1040
|
+
} catch (err) {
|
|
1041
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1042
|
+
throw new Error(`Failed to write ${CLAUDE_SETTINGS_DIR}/${CLAUDE_SETTINGS_FILE}: ${message}`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Generate .claude/settings.json from .toolkit/settings.yaml and mcp-config.yaml
|
|
1047
|
+
*/
|
|
1048
|
+
const syncCommand = new Command("sync").description("Generate .claude/settings.json from .toolkit/settings.yaml and mcp-config.yaml").action(async () => {
|
|
1049
|
+
try {
|
|
1050
|
+
const [workspaceRoot, config] = await Promise.all([TemplatesManagerService.getWorkspaceRoot(), TemplatesManagerService.readToolkitConfig()]);
|
|
1051
|
+
if (!config) throw new Error("No .toolkit/settings.yaml found. Run `aicode init` first.");
|
|
1052
|
+
if (hasHookConfig(config)) {
|
|
1053
|
+
await writeClaudeSettings(config, workspaceRoot);
|
|
1054
|
+
print.success("Written .claude/settings.json");
|
|
1055
|
+
} else print.warning("No hook.claude-code config found in settings.yaml — skipping");
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1058
|
+
print.error(`sync failed: ${message}`);
|
|
1059
|
+
process.exit(1);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
|
|
898
1063
|
//#endregion
|
|
899
1064
|
//#region src/cli.ts
|
|
900
1065
|
/**
|
|
901
1066
|
* Main entry point
|
|
902
1067
|
*/
|
|
903
1068
|
async function main() {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
const program = new Command();
|
|
1071
|
+
program.name("aicode").description("AI-powered code toolkit CLI for scaffolding, architecture management, and development workflows").version(version);
|
|
1072
|
+
program.addCommand(initCommand);
|
|
1073
|
+
program.addCommand(addCommand);
|
|
1074
|
+
program.addCommand(syncCommand);
|
|
1075
|
+
await program.parseAsync(process.argv);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1078
|
+
console.error(`aicode: ${message}`);
|
|
1079
|
+
process.exit(1);
|
|
1080
|
+
}
|
|
909
1081
|
}
|
|
910
1082
|
main();
|
|
911
1083
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agiflowai/aicode-toolkit",
|
|
3
3
|
"description": "AI-powered code toolkit CLI for scaffolding, architecture management, and development workflows",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.22",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"author": "AgiflowIO",
|
|
7
7
|
"repository": {
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"pino-pretty": "^13.1.1",
|
|
51
51
|
"xstate": "^5.23.0",
|
|
52
52
|
"zod": "3.25.76",
|
|
53
|
-
"@agiflowai/aicode-utils": "1.0.
|
|
54
|
-
"@agiflowai/coding-agent-bridge": "1.0.
|
|
53
|
+
"@agiflowai/aicode-utils": "1.0.16",
|
|
54
|
+
"@agiflowai/coding-agent-bridge": "1.0.19"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/express": "^5.0.0",
|