@webpresso/agent-kit 0.28.0 → 0.29.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -3
- package/README.md +2 -2
- package/bin/_run.js +6 -0
- package/bin/wp +5 -0
- package/catalog/base-kit/.github/actions/setup-webpresso/action.yml.tmpl +21 -0
- package/catalog/base-kit/.github/workflows/{ci.webpresso.yml.tmpl → ci.yml.tmpl} +17 -7
- package/catalog/base-kit/tsconfig.json.tmpl +1 -1
- package/catalog/docs/templates/blueprint.yaml +1 -1
- package/dist/esm/audit/_budgets.d.ts +9 -1
- package/dist/esm/audit/_budgets.js +8 -1
- package/dist/esm/audit/blueprint-db-consistency.js +2 -2
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +17 -7
- package/dist/esm/audit/blueprint-lifecycle-sql.js +298 -48
- package/dist/esm/audit/blueprint-readme-drift.d.ts +6 -0
- package/dist/esm/audit/blueprint-readme-drift.js +110 -0
- package/dist/esm/audit/no-first-party-mjs.js +5 -4
- package/dist/esm/audit/package-surface.js +79 -10
- package/dist/esm/audit/repo-guardrails.d.ts +1 -1
- package/dist/esm/audit/repo-guardrails.js +43 -3
- package/dist/esm/audit/tech-debt-cadence.js +2 -3
- package/dist/esm/audit/toolchain-isolation.js +2 -3
- package/dist/esm/blueprint/core/parser.js +3 -2
- package/dist/esm/blueprint/core/schema.d.ts +3 -2
- package/dist/esm/blueprint/core/schema.js +1 -1
- package/dist/esm/blueprint/cross-repo/audit.js +3 -4
- package/dist/esm/blueprint/db/cold-start.js +2 -3
- package/dist/esm/blueprint/db/enums.d.ts +1 -1
- package/dist/esm/blueprint/db/ephemeral-projection.d.ts +25 -0
- package/dist/esm/blueprint/db/ephemeral-projection.js +36 -0
- package/dist/esm/blueprint/db/gc.d.ts +11 -0
- package/dist/esm/blueprint/db/gc.js +55 -0
- package/dist/esm/blueprint/db/ingester.js +39 -1
- package/dist/esm/blueprint/db/migrations/run.js +5 -3
- package/dist/esm/blueprint/db/paths.d.ts +13 -24
- package/dist/esm/blueprint/db/paths.js +25 -33
- package/dist/esm/blueprint/execution/progress-bridge.js +5 -4
- package/dist/esm/blueprint/freshness.d.ts +2 -0
- package/dist/esm/blueprint/freshness.js +3 -1
- package/dist/esm/blueprint/lifecycle/audit.js +6 -6
- package/dist/esm/blueprint/lifecycle/engine.d.ts +1 -1
- package/dist/esm/blueprint/lifecycle/engine.js +13 -9
- package/dist/esm/blueprint/lifecycle/transition-matrix.d.ts +5 -0
- package/dist/esm/blueprint/lifecycle/transition-matrix.js +20 -0
- package/dist/esm/blueprint/markdown/helpers.d.ts +1 -1
- package/dist/esm/blueprint/projection-ready.js +2 -0
- package/dist/esm/blueprint/service/BlueprintService.js +1 -1
- package/dist/esm/blueprint/service/blueprint-records.js +1 -1
- package/dist/esm/blueprint/tracked-document/parser.js +1 -1
- package/dist/esm/blueprint/utils/archive.d.ts +2 -2
- package/dist/esm/blueprint/utils/archive.js +5 -2
- package/dist/esm/blueprint/utils/package-assets.d.ts +13 -0
- package/dist/esm/blueprint/utils/package-assets.js +38 -6
- package/dist/esm/build/normalize-tsconfig-json-exports.d.ts +13 -0
- package/dist/esm/build/normalize-tsconfig-json-exports.js +39 -0
- package/dist/esm/build/package-manifest.js +12 -4
- package/dist/esm/build/release-policy.d.ts +9 -18
- package/dist/esm/build/release-policy.js +10 -19
- package/dist/esm/build/runtime-surface-policy.d.ts +14 -0
- package/dist/esm/build/runtime-surface-policy.js +13 -0
- package/dist/esm/cli/commands/audit-core.d.ts +2 -2
- package/dist/esm/cli/commands/audit.js +7 -3
- package/dist/esm/cli/commands/blueprint/db-commands.js +0 -3
- package/dist/esm/cli/commands/blueprint/mutations.d.ts +3 -2
- package/dist/esm/cli/commands/blueprint/mutations.js +45 -39
- package/dist/esm/cli/commands/blueprint/router-output.js +2 -2
- package/dist/esm/cli/commands/doctor.d.ts +1 -1
- package/dist/esm/cli/commands/doctor.js +4 -5
- package/dist/esm/cli/commands/init/config.d.ts +6 -10
- package/dist/esm/cli/commands/init/config.js +36 -20
- package/dist/esm/cli/commands/init/gitignore-patcher.js +0 -1
- package/dist/esm/cli/commands/init/index.d.ts +8 -1
- package/dist/esm/cli/commands/init/index.js +17 -19
- package/dist/esm/cli/commands/init/package-root.d.ts +20 -0
- package/dist/esm/cli/commands/init/package-root.js +110 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +5 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +8 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +9 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +79 -1
- package/dist/esm/cli/commands/init/scaffolders/claude-rules/index.js +2 -12
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +2 -12
- package/dist/esm/config/tsconfig/cloudflare.json +1 -1
- package/dist/esm/config/tsconfig/library.json +1 -1
- package/dist/esm/config/tsconfig/react-library.json +3 -2
- package/dist/esm/config/tsconfig/react-router.json +1 -1
- package/dist/esm/dev/restore-dev-links/index.js +3 -4
- package/dist/esm/docs-linter/blueprint-plan.js +46 -4
- package/dist/esm/hooks/check-dev-link/index.js +3 -4
- package/dist/esm/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +174 -30
- package/dist/esm/hooks/guard-switch/index.js +3 -5
- package/dist/esm/hooks/post-tool/lint-after-edit.js +4 -5
- package/dist/esm/hooks/pretool-guard/index.js +2 -4
- package/dist/esm/hooks/pretool-guard/runner.js +2 -4
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +47 -6
- package/dist/esm/hooks/sessionstart/index.js +3 -4
- package/dist/esm/hooks/shared/direct-entrypoint.d.ts +10 -0
- package/dist/esm/hooks/shared/direct-entrypoint.js +21 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +3 -5
- package/dist/esm/hooks/test-quality-check.js +3 -4
- package/dist/esm/mcp/blueprint-server.js +26 -3
- package/dist/esm/mcp/cli.js +2 -6
- package/dist/esm/mcp/server.d.ts +2 -0
- package/dist/esm/mcp/server.js +18 -3
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/audit-kinds.js +1 -0
- package/dist/esm/mcp/tools/audit.d.ts +2 -1
- package/dist/esm/mcp/tools/audit.js +13 -3
- package/dist/esm/package.json +2 -0
- package/package.json +24 -15
- package/tsconfig/cloudflare.json +1 -1
- package/tsconfig/library.json +1 -1
- package/tsconfig/react-library.json +3 -2
- package/tsconfig/react-router.json +1 -1
- package/dist/esm/blueprint/db/legacy-migration.d.ts +0 -41
- package/dist/esm/blueprint/db/legacy-migration.js +0 -122
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { basename, dirname, join, relative } from 'node:path';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
11
|
import { isTelemetryEnabled, reportTthw } from '#telemetry/setup-tthw';
|
|
13
12
|
import { readPackageVersion } from '#cli/utils';
|
|
14
13
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
@@ -26,6 +25,7 @@ import { GENERATED_PATHS_BLOCK, patchGitignore, untrackGeneratedGitignoredPaths,
|
|
|
26
25
|
import { scaffoldAgentsMd } from './scaffold-agents-md.js';
|
|
27
26
|
import { scaffoldBlueprints } from './scaffold-blueprints.js';
|
|
28
27
|
import { scaffoldDocs } from './scaffold-docs.js';
|
|
28
|
+
import { resolveAgentKitPackageRoot } from './package-root.js';
|
|
29
29
|
import { BASE_KIT_QUALITY_TARGETS, collectRuntimeContractGuidance, scaffoldBaseKit, } from './scaffold-base-kit.js';
|
|
30
30
|
import { scaffoldMonorepoNav } from './scaffold-monorepo-nav.js';
|
|
31
31
|
import { REQUIRED_CORE_CAPABILITIES, auditHostSkillVisibility, parseAgentHosts, serializeHostVisibility, summarizeHostVisibility, } from './host-visibility.js';
|
|
@@ -75,23 +75,14 @@ export const EXIT_SUCCESS = 0;
|
|
|
75
75
|
export const EXIT_SETUP_FAIL = 1;
|
|
76
76
|
export const EXIT_USER_ABORT = 2;
|
|
77
77
|
export const EXIT_WRITE_FAIL = 3;
|
|
78
|
-
export function resolveCatalogDir() {
|
|
79
|
-
// The `
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const candidate = join(dir, 'catalog');
|
|
87
|
-
if (existsSync(candidate))
|
|
88
|
-
return candidate;
|
|
89
|
-
}
|
|
90
|
-
const parent = dirname(dir);
|
|
91
|
-
if (parent === dir)
|
|
92
|
-
break;
|
|
93
|
-
dir = parent;
|
|
94
|
-
}
|
|
78
|
+
export function resolveCatalogDir(options = {}) {
|
|
79
|
+
// The published native `bin/wp` binary executes from its own on-disk path,
|
|
80
|
+
// while the bundled module URL can resolve inside Bun's virtual filesystem.
|
|
81
|
+
// Probe both the module location and the real executable path so packed
|
|
82
|
+
// installs still find the shipped `catalog/` directory.
|
|
83
|
+
const root = resolveAgentKitPackageRoot({ ...options, requireCatalog: true });
|
|
84
|
+
if (root)
|
|
85
|
+
return join(root, 'catalog');
|
|
95
86
|
throw new Error('wp init: could not locate the webpresso catalog directory. The package may be broken.');
|
|
96
87
|
}
|
|
97
88
|
function inferBlueprintsDirOverride(repoRoot, existingConfig) {
|
|
@@ -184,7 +175,10 @@ export async function runInit(flags) {
|
|
|
184
175
|
const presets = parsePresets(flags.with);
|
|
185
176
|
let selectedHosts;
|
|
186
177
|
try {
|
|
187
|
-
selectedHosts =
|
|
178
|
+
selectedHosts =
|
|
179
|
+
flags.host === undefined
|
|
180
|
+
? (existingConfig?.hosts?.selected ?? parseAgentHosts(undefined))
|
|
181
|
+
: parseAgentHosts(flags.host);
|
|
188
182
|
}
|
|
189
183
|
catch (error) {
|
|
190
184
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -557,6 +551,10 @@ export async function runInit(flags) {
|
|
|
557
551
|
console.warn(` agent-kit global: ⚠ \`${agentKitGlobalResult.command.join(' ')}\` exited with ${agentKitGlobalResult.exitCode}; ` +
|
|
558
552
|
'the existing global binary is unchanged. Re-run `wp setup` once the registry is reachable.');
|
|
559
553
|
break;
|
|
554
|
+
case 'agent-kit-global-staging-failed':
|
|
555
|
+
console.warn(` agent-kit global: ⚠ native bin/wp staging failed (${agentKitGlobalResult.reason}); ` +
|
|
556
|
+
'the Claude plugin may keep using the previous cached launcher until the runtime package is rebuilt/reinstalled.');
|
|
557
|
+
break;
|
|
560
558
|
}
|
|
561
559
|
}
|
|
562
560
|
const claudePluginResult = ensureClaudeCodeUserPlugin({
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ResolveAgentKitPackageRootOptions {
|
|
2
|
+
readonly moduleUrl?: string;
|
|
3
|
+
readonly execPath?: string;
|
|
4
|
+
readonly argv0?: string;
|
|
5
|
+
readonly argv1?: string;
|
|
6
|
+
readonly pathEnv?: string;
|
|
7
|
+
readonly pathExtEnv?: string;
|
|
8
|
+
readonly platform?: NodeJS.Platform;
|
|
9
|
+
readonly requireCatalog?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function isAgentKitPackageRoot(dir: string, options?: {
|
|
12
|
+
readonly requireCatalog?: boolean;
|
|
13
|
+
}): boolean;
|
|
14
|
+
export declare function findAgentKitPackageRoot(startPath: string | undefined, options?: {
|
|
15
|
+
readonly requireCatalog?: boolean;
|
|
16
|
+
readonly platform?: NodeJS.Platform;
|
|
17
|
+
}): string | null;
|
|
18
|
+
export declare function resolveAgentKitPackageRoot(options?: ResolveAgentKitPackageRootOptions): string | null;
|
|
19
|
+
export declare function resolveAgentKitPackageRootOrThrow(errorMessage: string, options?: ResolveAgentKitPackageRootOptions): string;
|
|
20
|
+
//# sourceMappingURL=package-root.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join, posix, win32 } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
function existingModulePath(moduleUrl) {
|
|
5
|
+
if (typeof moduleUrl !== 'string' || moduleUrl.length === 0)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
return fileURLToPath(moduleUrl);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function isRunnablePath(path) {
|
|
15
|
+
if (typeof path !== 'string' || path.length === 0)
|
|
16
|
+
return false;
|
|
17
|
+
return isAbsolute(path) || win32.isAbsolute(path) || path.includes('/') || path.includes('\\');
|
|
18
|
+
}
|
|
19
|
+
function pathModuleForPlatform(platform) {
|
|
20
|
+
return platform === 'win32' ? win32 : posix;
|
|
21
|
+
}
|
|
22
|
+
function pathDelimiterForPlatform(platform) {
|
|
23
|
+
return platform === 'win32' ? ';' : ':';
|
|
24
|
+
}
|
|
25
|
+
function resolveBinOnPath(binName, pathEnv, options = {}) {
|
|
26
|
+
if (binName.length === 0 || typeof pathEnv !== 'string' || pathEnv.length === 0)
|
|
27
|
+
return null;
|
|
28
|
+
const platform = options.platform ?? process.platform;
|
|
29
|
+
const pathExtEnv = options.pathExtEnv ?? process.env.PATHEXT;
|
|
30
|
+
const pathModule = pathModuleForPlatform(platform);
|
|
31
|
+
const candidates = platform === 'win32' && !/\.[^./\\]+$/u.test(binName)
|
|
32
|
+
? [
|
|
33
|
+
binName,
|
|
34
|
+
...(typeof pathExtEnv === 'string' && pathExtEnv.length > 0
|
|
35
|
+
? pathExtEnv
|
|
36
|
+
.split(';')
|
|
37
|
+
.map((entry) => entry.trim())
|
|
38
|
+
.filter((entry) => entry.length > 0)
|
|
39
|
+
.map((entry) => `${binName}${entry.toLowerCase()}`)
|
|
40
|
+
: [`${binName}.exe`, `${binName}.cmd`, `${binName}.bat`]),
|
|
41
|
+
]
|
|
42
|
+
: [binName];
|
|
43
|
+
for (const entry of pathEnv.split(pathDelimiterForPlatform(platform))) {
|
|
44
|
+
if (entry.length === 0)
|
|
45
|
+
continue;
|
|
46
|
+
for (const binCandidate of candidates) {
|
|
47
|
+
for (const candidate of new Set([pathModule.join(entry, binCandidate), join(entry, binCandidate)])) {
|
|
48
|
+
if (existsSync(candidate))
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
export function isAgentKitPackageRoot(dir, options = {}) {
|
|
56
|
+
if (!existsSync(join(dir, 'package.json')))
|
|
57
|
+
return false;
|
|
58
|
+
if (options.requireCatalog === true && !existsSync(join(dir, 'catalog')))
|
|
59
|
+
return false;
|
|
60
|
+
return (existsSync(join(dir, 'bin', 'wp')) ||
|
|
61
|
+
existsSync(join(dir, 'bin', 'wp.cmd')) ||
|
|
62
|
+
existsSync(join(dir, 'bin', 'wp.exe')) ||
|
|
63
|
+
existsSync(join(dir, 'bin', 'wp.js')) ||
|
|
64
|
+
existsSync(join(dir, '.claude-plugin', 'plugin.json')) ||
|
|
65
|
+
existsSync(join(dir, 'src', 'cli', 'cli.ts')) ||
|
|
66
|
+
existsSync(join(dir, 'dist', 'esm', 'cli', 'cli.js')));
|
|
67
|
+
}
|
|
68
|
+
export function findAgentKitPackageRoot(startPath, options = {}) {
|
|
69
|
+
if (!isRunnablePath(startPath))
|
|
70
|
+
return null;
|
|
71
|
+
const platform = options.platform ?? process.platform;
|
|
72
|
+
const pathModule = pathModuleForPlatform(platform);
|
|
73
|
+
let dir = pathModule.dirname(startPath);
|
|
74
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
75
|
+
if (isAgentKitPackageRoot(dir, options))
|
|
76
|
+
return dir;
|
|
77
|
+
const parent = pathModule.dirname(dir);
|
|
78
|
+
if (parent === dir)
|
|
79
|
+
break;
|
|
80
|
+
dir = parent;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
export function resolveAgentKitPackageRoot(options = {}) {
|
|
85
|
+
const modulePath = existingModulePath(options.moduleUrl ?? import.meta.url);
|
|
86
|
+
const execPath = options.execPath ?? process.execPath;
|
|
87
|
+
const argv0 = options.argv0 ?? process.argv[0];
|
|
88
|
+
const argv1 = options.argv1 ?? process.argv[1];
|
|
89
|
+
const pathEnv = options.pathEnv ?? process.env.PATH;
|
|
90
|
+
const platform = options.platform ?? process.platform;
|
|
91
|
+
const pathModule = pathModuleForPlatform(platform);
|
|
92
|
+
const pathResolvedBin = resolveBinOnPath(pathModule.basename(argv0 || 'wp'), pathEnv, {
|
|
93
|
+
pathExtEnv: options.pathExtEnv,
|
|
94
|
+
platform,
|
|
95
|
+
});
|
|
96
|
+
const requireCatalog = options.requireCatalog;
|
|
97
|
+
for (const startPath of [modulePath, argv1, execPath, pathResolvedBin, argv0]) {
|
|
98
|
+
const root = findAgentKitPackageRoot(startPath ?? undefined, { requireCatalog, platform });
|
|
99
|
+
if (root)
|
|
100
|
+
return root;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
export function resolveAgentKitPackageRootOrThrow(errorMessage, options = {}) {
|
|
105
|
+
const root = resolveAgentKitPackageRoot(options);
|
|
106
|
+
if (root)
|
|
107
|
+
return root;
|
|
108
|
+
throw new Error(errorMessage);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=package-root.js.map
|
|
@@ -38,7 +38,11 @@ const TEMPLATE_MAP = [
|
|
|
38
38
|
],
|
|
39
39
|
['.husky/pre-commit.tmpl', '.husky/pre-commit'],
|
|
40
40
|
['.husky/commit-msg.tmpl', '.husky/commit-msg'],
|
|
41
|
-
[
|
|
41
|
+
[
|
|
42
|
+
'.github/actions/setup-webpresso/action.yml.tmpl',
|
|
43
|
+
'.github/actions/setup-webpresso/action.yml',
|
|
44
|
+
],
|
|
45
|
+
['.github/workflows/ci.yml.tmpl', '.github/workflows/ci.yml'],
|
|
42
46
|
['test/.gitkeep.tmpl', 'test/.gitkeep'],
|
|
43
47
|
['e2e/.gitkeep.tmpl', 'e2e/.gitkeep'],
|
|
44
48
|
];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type MergeOptions, type MergeResult } from '#cli/commands/init/merge';
|
|
2
|
+
import { type ResolveAgentKitPackageRootOptions } from '#cli/commands/init/package-root';
|
|
2
3
|
import type { CodexAppServerApi } from '#codex/app-server/types.js';
|
|
3
4
|
import { type SyncCodexHookTrustResult } from './codex-trust-sync.js';
|
|
4
5
|
type HookEntry = {
|
|
@@ -63,6 +64,8 @@ export interface ScaffoldAgentHooksResult {
|
|
|
63
64
|
codex: MergeResult;
|
|
64
65
|
claudeUser: MergeResult;
|
|
65
66
|
}
|
|
67
|
+
export type ResolvePackageRootForHookLaunchersOptions = ResolveAgentKitPackageRootOptions;
|
|
68
|
+
export declare function resolvePackageRootForHookLaunchers(options?: ResolvePackageRootForHookLaunchersOptions): string;
|
|
66
69
|
export declare function scaffoldAgentHooks(input: ScaffoldAgentHooksInput): Promise<ScaffoldAgentHooksResult>;
|
|
67
70
|
export {};
|
|
68
71
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
|
-
import {
|
|
15
|
-
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { join, resolve } from 'node:path';
|
|
16
15
|
import { isHookName } from '#cli/commands/hook.js';
|
|
17
16
|
import { patchJsonFile } from '#cli/commands/init/merge';
|
|
17
|
+
import { resolveAgentKitPackageRootOrThrow, } from '#cli/commands/init/package-root';
|
|
18
18
|
import { CodexAppServerClient } from '#codex/app-server/client.js';
|
|
19
19
|
import { normalizeGlobalCodexHooksFile, resolveBinaryOnPath, } from '#cli/commands/init/scaffolders/agent-hooks/codex-global-normalize';
|
|
20
20
|
import { isPresetOwnedGlobalCodexHook } from './codex-global-ownership.js';
|
|
21
21
|
import { syncCodexHookTrustWithAppServer, } from './codex-trust-sync.js';
|
|
22
22
|
import { buildSkillTag, extractSkillHooks, isTaggedSkillHook, } from './skill-hooks.js';
|
|
23
|
+
import { resolveRuntimeTarget, runtimePackageDirName } from '#build/runtime-targets.js';
|
|
23
24
|
// Claude Code uses $CLAUDE_PROJECT_DIR. Codex hook runners can execute while the
|
|
24
25
|
// active session cwd points at a sibling repo, so Codex hook commands must be
|
|
25
26
|
// path-stable and not depend on the caller's cwd.
|
|
@@ -609,31 +610,14 @@ function resolveProjectHookBinPath(repoRoot, binName) {
|
|
|
609
610
|
return resolve(repoRoot, 'node_modules', '@webpresso', 'agent-kit', 'bin', `${binName}.js`);
|
|
610
611
|
}
|
|
611
612
|
function resolvePackageHookBin(binName) {
|
|
612
|
-
return join(
|
|
613
|
+
return join(resolvePackageRootForHookLaunchers(), 'bin', `${binName}.js`);
|
|
613
614
|
}
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
for (let depth = 0; depth < 10; depth++) {
|
|
617
|
-
if (existsSync(join(dir, 'package.json')) && existsSync(join(dir, 'bin', 'wp.js'))) {
|
|
618
|
-
return dir;
|
|
619
|
-
}
|
|
620
|
-
const parent = dirname(dir);
|
|
621
|
-
if (parent === dir)
|
|
622
|
-
break;
|
|
623
|
-
dir = parent;
|
|
624
|
-
}
|
|
625
|
-
throw new Error('wp setup: could not locate @webpresso/agent-kit package root for hook launchers.');
|
|
615
|
+
export function resolvePackageRootForHookLaunchers(options = {}) {
|
|
616
|
+
return resolveAgentKitPackageRootOrThrow('wp setup: could not locate @webpresso/agent-kit package root for hook launchers.', options);
|
|
626
617
|
}
|
|
627
|
-
// Consumer-side runtime package directory for the current platform. Mirrors the
|
|
628
|
-
// canonical target table in `src/build/runtime-targets.ts`; kept inline here to
|
|
629
|
-
// avoid a deep cross-tree import for a small lookup. If that target list ever
|
|
630
|
-
// changes, update both. Returns undefined for platforms with no compiled
|
|
631
|
-
// runtime package.
|
|
632
618
|
function compiledRuntimePackageDir() {
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
const known = new Set(['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64', 'windows-x64']);
|
|
636
|
-
return known.has(id) ? `agent-kit-runtime-${id}` : undefined;
|
|
619
|
+
const target = resolveRuntimeTarget();
|
|
620
|
+
return target ? runtimePackageDirName(target.packageName) : undefined;
|
|
637
621
|
}
|
|
638
622
|
/**
|
|
639
623
|
* Absolute path to the consumer's self-contained compiled `wp` binary, when the
|
|
@@ -37,12 +37,17 @@ export interface EnsureAgentKitGlobalInput {
|
|
|
37
37
|
argv1?: string;
|
|
38
38
|
/** DI seam for source/git-clone detection. */
|
|
39
39
|
detectGit?: (argv1: string) => string | null;
|
|
40
|
+
/** DI seam for tests/global installs; defaults to the package root owning argv1/import. */
|
|
41
|
+
packageRoot?: string;
|
|
42
|
+
/** DI seam for staging-root fallback when argv1 cannot be mapped back to the owning package. */
|
|
43
|
+
resolvePackageRootForStaging?: (argv1: string) => string | null;
|
|
40
44
|
/** DI seam for spinner. Defaults to noop when !process.stdout.isTTY. */
|
|
41
45
|
spinnerFactory?: SpinnerFactory;
|
|
42
46
|
}
|
|
43
47
|
export type EnsureAgentKitGlobalResult = {
|
|
44
48
|
kind: 'agent-kit-global-updated';
|
|
45
49
|
command: readonly string[];
|
|
50
|
+
stagedBin?: string;
|
|
46
51
|
} | {
|
|
47
52
|
kind: 'agent-kit-global-skipped-dry-run';
|
|
48
53
|
} | {
|
|
@@ -57,6 +62,10 @@ export type EnsureAgentKitGlobalResult = {
|
|
|
57
62
|
kind: 'agent-kit-global-failed';
|
|
58
63
|
exitCode: number;
|
|
59
64
|
command: readonly string[];
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'agent-kit-global-staging-failed';
|
|
67
|
+
reason: string;
|
|
68
|
+
command: readonly string[];
|
|
60
69
|
};
|
|
61
70
|
/**
|
|
62
71
|
* Refresh the single global `@webpresso/agent-kit` install via `vp install -g`.
|
|
@@ -25,10 +25,58 @@
|
|
|
25
25
|
* warn-only contract as the codex-cli scaffolder).
|
|
26
26
|
*/
|
|
27
27
|
import { spawnSync } from 'node:child_process';
|
|
28
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, statSync, } from 'node:fs';
|
|
29
|
+
import { dirname, join } from 'node:path';
|
|
30
|
+
import { findAgentKitPackageRoot, resolveAgentKitPackageRoot, } from '#cli/commands/init/package-root';
|
|
28
31
|
import { makeNoopSpinnerFactory } from '#cli/commands/init/scaffolders/spinner';
|
|
29
32
|
import { buildVpGlobalInstallCommand, detectGitInstall, PUBLIC_PACKAGE_NAME, } from '#cli/auto-update/detect-pm.js';
|
|
30
33
|
const NO_VP_HINT = 'vp (vite-plus) is not on PATH; cannot refresh the global ' +
|
|
31
34
|
`${PUBLIC_PACKAGE_NAME}. Install vite-plus, then re-run \`wp setup\`.`;
|
|
35
|
+
function resolvePackageRootForStaging(argv1) {
|
|
36
|
+
const fromArgv = argv1.length > 0 ? findAgentKitPackageRoot(argv1) : null;
|
|
37
|
+
if (fromArgv)
|
|
38
|
+
return fromArgv;
|
|
39
|
+
return resolveAgentKitPackageRoot({ moduleUrl: import.meta.url });
|
|
40
|
+
}
|
|
41
|
+
function runtimeFilename(manifest, target) {
|
|
42
|
+
const binaryName = manifest.binaryName ?? 'wp';
|
|
43
|
+
return target.os === 'win32' ? `${binaryName}.exe` : binaryName;
|
|
44
|
+
}
|
|
45
|
+
function runtimePackageDirName(packageName) {
|
|
46
|
+
return packageName.split('/').at(-1) ?? packageName;
|
|
47
|
+
}
|
|
48
|
+
function resolveHostRuntimeBinary(packageRoot) {
|
|
49
|
+
const manifestPath = join(packageRoot, 'bin', 'runtime-manifest.json');
|
|
50
|
+
if (!existsSync(manifestPath))
|
|
51
|
+
return null;
|
|
52
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
53
|
+
const target = manifest.targets?.find((candidate) => candidate.os === process.platform && candidate.cpu === process.arch);
|
|
54
|
+
if (!target?.id || !target.packageName)
|
|
55
|
+
return null;
|
|
56
|
+
const filename = runtimeFilename(manifest, target);
|
|
57
|
+
const candidates = [
|
|
58
|
+
join(packageRoot, 'bin', 'runtime', target.id, filename),
|
|
59
|
+
join(packageRoot, 'dist', 'runtime', target.id, filename),
|
|
60
|
+
join(packageRoot, '..', runtimePackageDirName(target.packageName), 'bin', filename),
|
|
61
|
+
join(packageRoot, 'node_modules', '@webpresso', runtimePackageDirName(target.packageName), 'bin', filename),
|
|
62
|
+
];
|
|
63
|
+
const source = candidates.find((candidate) => existsSync(candidate));
|
|
64
|
+
return source ? { source, targetId: target.id } : null;
|
|
65
|
+
}
|
|
66
|
+
function stageHostRuntimeLauncher(packageRoot) {
|
|
67
|
+
const runtime = resolveHostRuntimeBinary(packageRoot);
|
|
68
|
+
if (!runtime)
|
|
69
|
+
return null;
|
|
70
|
+
const destination = join(packageRoot, 'bin', 'wp');
|
|
71
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
72
|
+
copyFileSync(runtime.source, destination);
|
|
73
|
+
chmodSync(destination, 0o755);
|
|
74
|
+
const stat = statSync(destination);
|
|
75
|
+
if (!stat.isFile()) {
|
|
76
|
+
throw new Error(`staged ${destination} for ${runtime.targetId} is not a regular file`);
|
|
77
|
+
}
|
|
78
|
+
return destination;
|
|
79
|
+
}
|
|
32
80
|
/**
|
|
33
81
|
* Refresh the single global `@webpresso/agent-kit` install via `vp install -g`.
|
|
34
82
|
*/
|
|
@@ -58,7 +106,37 @@ export function ensureAgentKitGlobal(input) {
|
|
|
58
106
|
spinner.fail('agent-kit global refresh failed');
|
|
59
107
|
return { kind: 'agent-kit-global-failed', exitCode: install.status ?? -1, command };
|
|
60
108
|
}
|
|
109
|
+
let stagedBin;
|
|
110
|
+
const packageRoot = input.packageRoot ??
|
|
111
|
+
(input.resolvePackageRootForStaging ?? resolvePackageRootForStaging)(argv1);
|
|
112
|
+
if (!packageRoot) {
|
|
113
|
+
spinner.fail('agent-kit native launcher staging failed');
|
|
114
|
+
return {
|
|
115
|
+
kind: 'agent-kit-global-staging-failed',
|
|
116
|
+
reason: 'could not resolve the owning @webpresso/agent-kit package root for staging',
|
|
117
|
+
command,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
stagedBin = stageHostRuntimeLauncher(packageRoot) ?? undefined;
|
|
122
|
+
if (!stagedBin) {
|
|
123
|
+
spinner.fail('agent-kit native launcher staging failed');
|
|
124
|
+
return {
|
|
125
|
+
kind: 'agent-kit-global-staging-failed',
|
|
126
|
+
reason: `could not resolve a host runtime binary under ${packageRoot}`,
|
|
127
|
+
command,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
spinner.fail('agent-kit native launcher staging failed');
|
|
133
|
+
return {
|
|
134
|
+
kind: 'agent-kit-global-staging-failed',
|
|
135
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
136
|
+
command,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
61
139
|
spinner.succeed('agent-kit global up to date');
|
|
62
|
-
return { kind: 'agent-kit-global-updated', command };
|
|
140
|
+
return { kind: 'agent-kit-global-updated', command, stagedBin };
|
|
63
141
|
}
|
|
64
142
|
//# sourceMappingURL=index.js.map
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync, } from 'node:fs';
|
|
6
6
|
import { dirname, join, relative } from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
7
|
import { readConfig } from '#cli/commands/init/config';
|
|
9
8
|
import { readPackageJson } from '#cli/commands/init/detect-consumer';
|
|
9
|
+
import { resolveAgentKitPackageRootOrThrow } from '#cli/commands/init/package-root';
|
|
10
10
|
function detectMode(repoRoot) {
|
|
11
11
|
const pkg = readPackageJson(repoRoot).info;
|
|
12
12
|
if (pkg?.name === 'webpresso') {
|
|
@@ -58,17 +58,7 @@ function writeOverrideRule(targetPath, sourcePath, options) {
|
|
|
58
58
|
return { targetPath, action: 'drifted' };
|
|
59
59
|
}
|
|
60
60
|
function resolveCurrentPackageRoot() {
|
|
61
|
-
|
|
62
|
-
for (let depth = 0; depth < 8; depth++) {
|
|
63
|
-
if (existsSync(join(dir, 'package.json')) && existsSync(join(dir, 'catalog'))) {
|
|
64
|
-
return dir;
|
|
65
|
-
}
|
|
66
|
-
const parent = dirname(dir);
|
|
67
|
-
if (parent === dir)
|
|
68
|
-
break;
|
|
69
|
-
dir = parent;
|
|
70
|
-
}
|
|
71
|
-
throw new Error('wp init: could not locate the webpresso package root for claude-rules fallback.');
|
|
61
|
+
return resolveAgentKitPackageRootOrThrow('wp init: could not locate the webpresso package root for claude-rules fallback.', { requireCatalog: true });
|
|
72
62
|
}
|
|
73
63
|
export function scaffoldClaudeRules(input) {
|
|
74
64
|
const { repoRoot, options } = input;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, rmSync, symlinkSync, } from 'node:fs';
|
|
2
2
|
import { dirname, join, relative } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
3
|
import { readPackageJson } from '#cli/commands/init/detect-consumer';
|
|
4
|
+
import { resolveAgentKitPackageRootOrThrow } from '#cli/commands/init/package-root';
|
|
5
5
|
function detectMode(repoRoot) {
|
|
6
6
|
const pkg = readPackageJson(repoRoot).info;
|
|
7
7
|
if (pkg?.name === 'webpresso') {
|
|
@@ -24,17 +24,7 @@ function detectMode(repoRoot) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
function resolveCurrentPackageRoot() {
|
|
27
|
-
|
|
28
|
-
for (let depth = 0; depth < 8; depth++) {
|
|
29
|
-
if (existsSync(join(dir, 'package.json')) && existsSync(join(dir, 'catalog'))) {
|
|
30
|
-
return dir;
|
|
31
|
-
}
|
|
32
|
-
const parent = dirname(dir);
|
|
33
|
-
if (parent === dir)
|
|
34
|
-
break;
|
|
35
|
-
dir = parent;
|
|
36
|
-
}
|
|
37
|
-
throw new Error('wp init: could not locate the webpresso package root for subagents fallback.');
|
|
27
|
+
return resolveAgentKitPackageRootOrThrow('wp init: could not locate the webpresso package root for subagents fallback.', { requireCatalog: true });
|
|
38
28
|
}
|
|
39
29
|
export function scaffoldSubagents(input) {
|
|
40
30
|
const { repoRoot, options } = input;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
3
|
"display": "React Library",
|
|
4
|
-
"extends": "
|
|
4
|
+
"extends": "@webpresso/agent-kit/tsconfig/library.json",
|
|
5
5
|
"compilerOptions": {
|
|
6
6
|
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
7
|
-
"jsx": "react-jsx"
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"types": ["react", "react-dom"]
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
3
|
"display": "React Router v7",
|
|
4
|
-
"extends": "
|
|
4
|
+
"extends": "@webpresso/agent-kit/tsconfig/react-library.json",
|
|
5
5
|
"compilerOptions": {
|
|
6
6
|
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
7
7
|
"jsx": "react-jsx",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
* to fail in CI. Postinstall is the only seam that runs deterministically
|
|
21
21
|
* after install, doesn't touch the lockfile, and is observable enough to debug.
|
|
22
22
|
*/
|
|
23
|
-
import { lstatSync, mkdirSync, readlinkSync,
|
|
23
|
+
import { lstatSync, mkdirSync, readlinkSync, renameSync, symlinkSync, unlinkSync, } from 'node:fs';
|
|
24
24
|
import { existsSync } from 'node:fs';
|
|
25
25
|
import { dirname, join } from 'node:path';
|
|
26
|
-
import { fileURLToPath } from 'node:url';
|
|
27
26
|
import { STATE_FILE_RELATIVE_PATH, readDevLinkState } from '#dev/dev-link-state';
|
|
27
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
28
28
|
export function restoreDevLinks(options = {}) {
|
|
29
29
|
const cwd = options.cwd ?? process.cwd();
|
|
30
30
|
const stdout = options.stdout ?? process.stdout;
|
|
@@ -95,8 +95,7 @@ function timestamp() {
|
|
|
95
95
|
const d = new Date();
|
|
96
96
|
return `${d.getFullYear()}${pad2(d.getMonth() + 1)}${pad2(d.getDate())}-${pad2(d.getHours())}${pad2(d.getMinutes())}${pad2(d.getSeconds())}`;
|
|
97
97
|
}
|
|
98
|
-
if (
|
|
99
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
98
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
100
99
|
const result = restoreDevLinks();
|
|
101
100
|
process.exit(result.exitCode);
|
|
102
101
|
}
|
|
@@ -24,7 +24,7 @@ const EXECUTABLE_BLUEPRINT_STATUSES = new Set([
|
|
|
24
24
|
'completed',
|
|
25
25
|
'archived',
|
|
26
26
|
]);
|
|
27
|
-
const TASK_STATUSES = new Set(['todo', '
|
|
27
|
+
const TASK_STATUSES = new Set(['todo', 'in-progress', 'blocked', 'done', 'dropped']);
|
|
28
28
|
/**
|
|
29
29
|
* Find tasks using ### (3 hashes) instead of #### (4 hashes).
|
|
30
30
|
* Exported for testability.
|
|
@@ -282,7 +282,7 @@ function createTaskStatusInvalidError(filePath, taskId, status) {
|
|
|
282
282
|
file: filePath,
|
|
283
283
|
severity: 'error',
|
|
284
284
|
source: 'blueprint-format',
|
|
285
|
-
message: `Task ${taskId} has invalid status "${status}". Use only: todo,
|
|
285
|
+
message: `Task ${taskId} has invalid status "${status}". Use only: todo, in-progress, blocked, done, dropped.`,
|
|
286
286
|
ruleId: 'blueprint-task-status-invalid',
|
|
287
287
|
};
|
|
288
288
|
}
|
|
@@ -318,7 +318,7 @@ function createCompletedRequiresAllDoneError(filePath, taskId, taskStatus) {
|
|
|
318
318
|
file: filePath,
|
|
319
319
|
severity: 'error',
|
|
320
320
|
source: 'blueprint-format',
|
|
321
|
-
message: `Blueprint status is completed but task ${taskId} is "${taskStatus}" (expected "done").`,
|
|
321
|
+
message: `Blueprint status is completed but task ${taskId} is "${taskStatus}" (expected "done" or "dropped").`,
|
|
322
322
|
ruleId: 'blueprint-completed-requires-all-done',
|
|
323
323
|
};
|
|
324
324
|
}
|
|
@@ -330,6 +330,48 @@ function validateLifecycleContract(filePath, content) {
|
|
|
330
330
|
errors.push(createBlueprintStatusError(filePath, status ?? '(missing)'));
|
|
331
331
|
return errors;
|
|
332
332
|
}
|
|
333
|
+
if (status !== 'draft') {
|
|
334
|
+
const title = frontmatter?.title?.trim();
|
|
335
|
+
const owner = frontmatter?.owner?.trim();
|
|
336
|
+
const complexity = extractComplexity(content);
|
|
337
|
+
const lastUpdated = frontmatter?.last_updated?.trim();
|
|
338
|
+
if (!title) {
|
|
339
|
+
errors.push({
|
|
340
|
+
file: filePath,
|
|
341
|
+
severity: 'error',
|
|
342
|
+
source: 'blueprint-format',
|
|
343
|
+
message: 'Blueprint is missing required frontmatter field: title.',
|
|
344
|
+
ruleId: 'blueprint-title-required',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (!owner) {
|
|
348
|
+
errors.push({
|
|
349
|
+
file: filePath,
|
|
350
|
+
severity: 'error',
|
|
351
|
+
source: 'blueprint-format',
|
|
352
|
+
message: 'Blueprint is missing required frontmatter field: owner.',
|
|
353
|
+
ruleId: 'blueprint-owner-required',
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (!['XS', 'S', 'M', 'L', 'XL'].includes(complexity)) {
|
|
357
|
+
errors.push({
|
|
358
|
+
file: filePath,
|
|
359
|
+
severity: 'error',
|
|
360
|
+
source: 'blueprint-format',
|
|
361
|
+
message: `Blueprint complexity "${complexity}" is invalid. Use only: XS, S, M, L, XL.`,
|
|
362
|
+
ruleId: 'blueprint-complexity-invalid',
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
if (!lastUpdated || !/^\d{4}-\d{2}-\d{2}$/.test(lastUpdated.replace(/^['"]|['"]$/g, ''))) {
|
|
366
|
+
errors.push({
|
|
367
|
+
file: filePath,
|
|
368
|
+
severity: 'error',
|
|
369
|
+
source: 'blueprint-format',
|
|
370
|
+
message: 'Blueprint is missing valid frontmatter field: last_updated (YYYY-MM-DD).',
|
|
371
|
+
ruleId: 'blueprint-last-updated-required',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
333
375
|
const taskBlocks = extractTaskBlocks(content);
|
|
334
376
|
for (const task of taskBlocks) {
|
|
335
377
|
const statusMatch = task.section.match(/\*\*Status:\*\*\s*(.+)/i);
|
|
@@ -364,7 +406,7 @@ function validateLifecycleContract(filePath, content) {
|
|
|
364
406
|
if (!taskStatus || !TASK_STATUSES.has(taskStatus)) {
|
|
365
407
|
continue;
|
|
366
408
|
}
|
|
367
|
-
if (taskStatus !== 'done') {
|
|
409
|
+
if (taskStatus !== 'done' && taskStatus !== 'dropped') {
|
|
368
410
|
errors.push(createCompletedRequiresAllDoneError(filePath, task.id, taskStatus));
|
|
369
411
|
}
|
|
370
412
|
}
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* instead of the live source declared in `.webpresso/webpresso-dev-link.json`.
|
|
14
14
|
* Always exits 0; never blocks session start.
|
|
15
15
|
*/
|
|
16
|
-
import { readlinkSync
|
|
16
|
+
import { readlinkSync } from 'node:fs';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
18
|
import { STATE_FILE_RELATIVE_PATH, readDevLinkState } from '#dev/dev-link-state';
|
|
19
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
20
20
|
export function detectDevLinkBreakage(options = {}) {
|
|
21
21
|
const cwd = options.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
22
22
|
const state = readDevLinkState(cwd);
|
|
@@ -78,8 +78,7 @@ export async function main() {
|
|
|
78
78
|
process.stdout.write(`${out}\n`);
|
|
79
79
|
process.exit(0);
|
|
80
80
|
}
|
|
81
|
-
if (
|
|
82
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
81
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
83
82
|
void main();
|
|
84
83
|
}
|
|
85
84
|
// Re-export so tests can stub the state file path if needed in future.
|