@sabaiway/agent-workflow-kit 14.24.0 → 14.25.1
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/CHANGELOG.md +42 -0
- package/README.md +15 -4
- package/SKILL.md +1 -1
- package/bin/install.mjs +15 -15
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/bootstrap.md +5 -1
- package/references/modes/mcp.md +9 -4
- package/references/modes/recommendations.md +17 -9
- package/references/modes/upgrade.md +3 -2
- package/tools/init-preflight.mjs +92 -0
- package/tools/init-project.mjs +400 -0
- package/tools/mcp.mjs +80 -20
- package/tools/payload-prune.mjs +5 -0
- package/tools/recommendations.mjs +54 -62
- package/tools/write-lanes.mjs +171 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
|
+
import { dirCovers } from './declared-paths.mjs';
|
|
3
|
+
import { claudeDirOf, skillTargets } from './jev-facts.mjs';
|
|
4
|
+
import { targetsFor } from './jev-connect.mjs';
|
|
5
|
+
|
|
6
|
+
export const LANE_CONSOLE = 'console';
|
|
7
|
+
export const LANE_CHAT = 'chat';
|
|
8
|
+
|
|
9
|
+
export const VARIANT_LANES = Object.freeze({
|
|
10
|
+
'velocity-core': LANE_CONSOLE,
|
|
11
|
+
'kit-tools-tier': LANE_CONSOLE,
|
|
12
|
+
'bridge-tier': LANE_CONSOLE,
|
|
13
|
+
'autonomy-policy': LANE_CHAT,
|
|
14
|
+
'autonomy-render': LANE_CONSOLE,
|
|
15
|
+
'sandbox-provision': LANE_CONSOLE,
|
|
16
|
+
'sandbox-provision.installable': LANE_CONSOLE,
|
|
17
|
+
'review-recipe': LANE_CHAT,
|
|
18
|
+
'gates-declaration': LANE_CHAT,
|
|
19
|
+
'gates-inert': LANE_CHAT,
|
|
20
|
+
'gates-inert.no-verification': LANE_CHAT,
|
|
21
|
+
'gates-inert.producer-unrecognized': LANE_CHAT,
|
|
22
|
+
'gates-inert.coverage-domain-narrow': LANE_CHAT,
|
|
23
|
+
'source-size': LANE_CHAT,
|
|
24
|
+
'source-size.unminted': LANE_CHAT,
|
|
25
|
+
'source-size.adopted-elsewhere': LANE_CHAT,
|
|
26
|
+
'source-size.id-squatter': LANE_CHAT,
|
|
27
|
+
'gate-hook': LANE_CONSOLE,
|
|
28
|
+
'gate-hook.marker-stale': LANE_CONSOLE,
|
|
29
|
+
'commit-guard': LANE_CONSOLE,
|
|
30
|
+
enforcement: LANE_CONSOLE,
|
|
31
|
+
'read-lane': LANE_CHAT,
|
|
32
|
+
'read-lane.stale': LANE_CONSOLE,
|
|
33
|
+
'read-lane.missing': LANE_CONSOLE,
|
|
34
|
+
'state-block': LANE_CONSOLE,
|
|
35
|
+
'mcp-channel': LANE_CONSOLE,
|
|
36
|
+
'mcp-channel.differing': LANE_CONSOLE,
|
|
37
|
+
agents: LANE_CONSOLE,
|
|
38
|
+
'executor-vehicle': LANE_CONSOLE,
|
|
39
|
+
'family-freshness': LANE_CONSOLE,
|
|
40
|
+
'adr-store-migration': LANE_CHAT,
|
|
41
|
+
'sandbox-masks': LANE_CHAT,
|
|
42
|
+
'sandbox-masks.unfenced-mount': LANE_CHAT,
|
|
43
|
+
'sandbox-lane': LANE_CHAT,
|
|
44
|
+
'worktrees-dir': LANE_CHAT,
|
|
45
|
+
'spec-adoption': LANE_CHAT,
|
|
46
|
+
'profile-gap': LANE_CHAT,
|
|
47
|
+
'jev-connect': LANE_CONSOLE,
|
|
48
|
+
'jev-skill': LANE_CONSOLE,
|
|
49
|
+
'jev-skill.earlier': LANE_CONSOLE,
|
|
50
|
+
'spec-adoption.adopting': LANE_CHAT,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const laneOf = (variant) => {
|
|
54
|
+
if (!Object.hasOwn(VARIANT_LANES, variant)) {
|
|
55
|
+
throw new Error(`unregistered variant: ${variant}`);
|
|
56
|
+
}
|
|
57
|
+
return VARIANT_LANES[variant];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const SETTINGS_PATH = join('.claude', 'settings.json');
|
|
61
|
+
const PROTECTED_FILES = [
|
|
62
|
+
SETTINGS_PATH,
|
|
63
|
+
join('.claude', 'settings.local.json'),
|
|
64
|
+
'.mcp.json',
|
|
65
|
+
join('.git', 'config'),
|
|
66
|
+
];
|
|
67
|
+
const PROTECTED_DIRS = ['agents', 'hooks', 'skills'].map((name) => join('.claude', name));
|
|
68
|
+
|
|
69
|
+
export const isProtectedPath = (abs, { root, home, hooksDir }) => {
|
|
70
|
+
const project = resolve(root);
|
|
71
|
+
const hooks = resolve(hooksDir);
|
|
72
|
+
const path = resolve(abs);
|
|
73
|
+
if (!dirCovers(project, hooks)) {
|
|
74
|
+
throw new Error(`hooksDir outside the project: ${hooksDir}`);
|
|
75
|
+
}
|
|
76
|
+
if (!dirCovers(project, path)) return dirCovers(resolve(home), path);
|
|
77
|
+
return PROTECTED_FILES.some((file) => path === join(project, file))
|
|
78
|
+
|| [...PROTECTED_DIRS.map((dir) => join(project, dir)), hooks].some((dir) => dirCovers(dir, path));
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const ROUTE = "applies at your next npx @sabaiway/agent-workflow-kit@latest init, run from this project's folder";
|
|
82
|
+
const RESTART = '; if init reports it not pending, restart the agent from that console';
|
|
83
|
+
const SETTINGS_WRITES = [SETTINGS_PATH];
|
|
84
|
+
const HOOK_WRITES = [join('.claude', 'hooks', 'agent-workflow-gates.mjs'), ...SETTINGS_WRITES];
|
|
85
|
+
const MCP_WRITES = ['.mcp.json', ...SETTINGS_WRITES];
|
|
86
|
+
const AGENT_WRITES = [join('.claude', 'agents')];
|
|
87
|
+
|
|
88
|
+
const getSkillPaths = ({ env, home, platform }) => {
|
|
89
|
+
const claudeDir = claudeDirOf(env, home, platform).dir;
|
|
90
|
+
return skillTargets({ home, claudeDir }).map(({ path }) => path);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const getStartupPaths = ({ env, home, platform }) => targetsFor({
|
|
94
|
+
shell: env.SHELL,
|
|
95
|
+
home,
|
|
96
|
+
zdotdir: env.ZDOTDIR,
|
|
97
|
+
xdgConfigHome: env.XDG_CONFIG_HOME,
|
|
98
|
+
platform,
|
|
99
|
+
}).filter(({ path }) => path !== null).map(({ path }) => path);
|
|
100
|
+
|
|
101
|
+
const createForm = (file, preview, apply, writes, restart = false) => Object.freeze({
|
|
102
|
+
file,
|
|
103
|
+
preview,
|
|
104
|
+
apply,
|
|
105
|
+
writes,
|
|
106
|
+
route: restart ? ROUTE + RESTART : ROUTE,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const CORE_FORM = createForm('velocity-profile.mjs', ['--cwd', '<root>'], ['--apply', '--cwd', '<root>'], SETTINGS_WRITES);
|
|
110
|
+
const HOOK_FORM = createForm('gate-hook.mjs', ['--cwd', '<root>'], ['--apply', '--cwd', '<root>'], HOOK_WRITES);
|
|
111
|
+
const AGENT_FORM = createForm('cheap-agents.mjs', ['--cwd', '<root>'], ['--apply', '--cwd', '<root>'], AGENT_WRITES);
|
|
112
|
+
const SKILL_FORM = createForm('jev-skill.mjs', ['--claude-dir', '<dir>'], ['--apply', '--claude-dir', '<dir>'], getSkillPaths, true);
|
|
113
|
+
|
|
114
|
+
const FORMS = Object.freeze({
|
|
115
|
+
'velocity-core': CORE_FORM,
|
|
116
|
+
'kit-tools-tier': createForm('velocity-profile.mjs',
|
|
117
|
+
['--kit-tools', '--cwd', '<root>'], ['--apply', '--kit-tools', '--cwd', '<root>'], SETTINGS_WRITES),
|
|
118
|
+
'bridge-tier': createForm('velocity-profile.mjs',
|
|
119
|
+
['--bridge-tier', '--cwd', '<root>'], ['--apply', '--bridge-tier', '--cwd', '<root>'], SETTINGS_WRITES, true),
|
|
120
|
+
'autonomy-render': createForm('velocity-profile.mjs',
|
|
121
|
+
['--autonomy', '--cwd', '<root>'], ['--autonomy', '--apply', '--cwd', '<root>'], SETTINGS_WRITES),
|
|
122
|
+
'gate-hook': HOOK_FORM,
|
|
123
|
+
'read-lane.missing': HOOK_FORM,
|
|
124
|
+
'mcp-channel': createForm('mcp.mjs', ['--cwd', '<root>'], ['--apply', '--cwd', '<root>'], MCP_WRITES),
|
|
125
|
+
'mcp-channel.differing': createForm('mcp.mjs',
|
|
126
|
+
['--replace', '--cwd', '<root>'], ['--replace', '--apply', '--cwd', '<root>'], MCP_WRITES),
|
|
127
|
+
agents: AGENT_FORM,
|
|
128
|
+
'executor-vehicle': AGENT_FORM,
|
|
129
|
+
'sandbox-provision.installable': createForm('autonomy-doctor.mjs', [], ['--apply', '<tuple>'], [], true),
|
|
130
|
+
'jev-skill': SKILL_FORM,
|
|
131
|
+
'jev-skill.earlier': SKILL_FORM,
|
|
132
|
+
'jev-connect': createForm('jev-connect.mjs', null, [], getStartupPaths, true),
|
|
133
|
+
'family-freshness': { route: ROUTE },
|
|
134
|
+
'sandbox-provision': null,
|
|
135
|
+
'commit-guard': null,
|
|
136
|
+
'gate-hook.marker-stale': null,
|
|
137
|
+
'read-lane.stale': null,
|
|
138
|
+
'state-block': null,
|
|
139
|
+
enforcement: null,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const resolveArgument = (arg, facts) => {
|
|
143
|
+
if (arg === '<root>') return facts.root;
|
|
144
|
+
if (arg === '<tuple>') return facts.tuple;
|
|
145
|
+
if (arg === '<dir>') return claudeDirOf(facts.env, facts.home, facts.platform).dir;
|
|
146
|
+
return arg;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const consoleArgv = (variant, facts) => {
|
|
150
|
+
laneOf(variant);
|
|
151
|
+
const form = FORMS[variant];
|
|
152
|
+
if (!form?.file) return null;
|
|
153
|
+
const argv = (args) => ['node', join(facts.toolsDir, form.file), ...args.map((arg) => resolveArgument(arg, facts))];
|
|
154
|
+
return {
|
|
155
|
+
preview: form.preview === null ? null : argv(form.preview),
|
|
156
|
+
apply: argv(form.apply),
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const writesOf = (variant, facts) => {
|
|
161
|
+
laneOf(variant);
|
|
162
|
+
const form = FORMS[variant];
|
|
163
|
+
if (!form?.file) return null;
|
|
164
|
+
if (typeof form.writes === 'function') return form.writes(facts);
|
|
165
|
+
return form.writes.map((path) => join(facts.root, path));
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const routeLine = (variant) => {
|
|
169
|
+
laneOf(variant);
|
|
170
|
+
return FORMS[variant]?.route ?? null;
|
|
171
|
+
};
|