@habitat-ai/cli 0.2.0
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/bin/run.js +9 -0
- package/dist/application.d.ts +14 -0
- package/dist/application.js +18 -0
- package/dist/composition.d.ts +8 -0
- package/dist/composition.js +59 -0
- package/dist/generators/init.cjs +252 -0
- package/dist/generators/remove-hook.cjs +191 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/nx-plugin.js +51607 -0
- package/generators.json +15 -0
- package/generators.schema.json +6 -0
- package/oclif.manifest.json +4 -0
- package/package.json +111 -0
- package/src/nx-plugin.d.ts +4 -0
package/bin/run.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Inputs fixed by one Habitat process activation. */
|
|
2
|
+
export type ExecuteHabitatOptions = Readonly<{
|
|
3
|
+
appRoot: string;
|
|
4
|
+
workspaceRoot: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
development?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Runs one native Oclif invocation over the app-selected Habitat client.
|
|
10
|
+
*
|
|
11
|
+
* Oclif owns command discovery and dispatch; this boundary supplies only the
|
|
12
|
+
* ready workspace client required by the Habitat command plugin.
|
|
13
|
+
*/
|
|
14
|
+
export declare function executeHabitat({ appRoot, workspaceRoot, args, development, }: ExecuteHabitatOptions): Promise<unknown>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { bindHabitatClient } from "@habitat-ai/plugin-cli/binding";
|
|
2
|
+
import { execute, settings } from "@oclif/core";
|
|
3
|
+
import { createHabitatClientForWorkspace } from "./composition.js";
|
|
4
|
+
/**
|
|
5
|
+
* Runs one native Oclif invocation over the app-selected Habitat client.
|
|
6
|
+
*
|
|
7
|
+
* Oclif owns command discovery and dispatch; this boundary supplies only the
|
|
8
|
+
* ready workspace client required by the Habitat command plugin.
|
|
9
|
+
*/
|
|
10
|
+
export async function executeHabitat({ appRoot, workspaceRoot, args, development, }) {
|
|
11
|
+
settings.enableAutoTranspile = development === true;
|
|
12
|
+
const client = await createHabitatClientForWorkspace(workspaceRoot);
|
|
13
|
+
return execute({
|
|
14
|
+
...(args === undefined ? {} : { args }),
|
|
15
|
+
...(development === undefined ? {} : { development }),
|
|
16
|
+
loadOptions: bindHabitatClient({ root: appRoot }, client),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Client } from "@habitat-ai/service/client";
|
|
2
|
+
/**
|
|
3
|
+
* Constructs the production Habitat client for one workspace.
|
|
4
|
+
*
|
|
5
|
+
* The app selects concrete Node providers here so the service and both
|
|
6
|
+
* projections remain provider-neutral and share one composition boundary.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createHabitatClientForWorkspace(workspaceRoot: string): Promise<Client>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { NodeServices } from "@effect/platform-node";
|
|
3
|
+
import { makeNodeGritRuleEvaluationResource } from "@habitat-ai/resource-rule-evaluation/providers/grit-effect-platform-node";
|
|
4
|
+
import { makeNodeGitSourceInventoryResource } from "@habitat-ai/resource-source-inventory/providers/git-effect-platform-node";
|
|
5
|
+
import { createClient } from "@habitat-ai/service/client";
|
|
6
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
7
|
+
import { Type } from "typebox";
|
|
8
|
+
import { Validator } from "typebox/schema";
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const gritExecutable = require.resolve("@getgrit/cli/run-grit.js");
|
|
11
|
+
const HABITAT_BLUEPRINT_PACK = "@habitat-ai/blueprints";
|
|
12
|
+
const CommandTimeoutSchema = Type.Integer({ minimum: 1, maximum: 600_000 });
|
|
13
|
+
const commandTimeoutValidator = new Validator({}, CommandTimeoutSchema);
|
|
14
|
+
const commandTimeoutMs = decodeCommandTimeout(process.env.HABITAT_COMMAND_TIMEOUT_MS);
|
|
15
|
+
const deps = Effect.runPromise(Effect.gen(function* () {
|
|
16
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
17
|
+
const path = yield* Path.Path;
|
|
18
|
+
return {
|
|
19
|
+
fileSystem,
|
|
20
|
+
path,
|
|
21
|
+
ruleEvaluation: makeNodeGritRuleEvaluationResource({
|
|
22
|
+
executable: gritExecutable,
|
|
23
|
+
timeoutMs: commandTimeoutMs,
|
|
24
|
+
}),
|
|
25
|
+
sourceInventory: makeNodeGitSourceInventoryResource(),
|
|
26
|
+
};
|
|
27
|
+
}).pipe(Effect.provide(NodeServices.layer)));
|
|
28
|
+
/**
|
|
29
|
+
* Constructs the production Habitat client for one workspace.
|
|
30
|
+
*
|
|
31
|
+
* The app selects concrete Node providers here so the service and both
|
|
32
|
+
* projections remain provider-neutral and share one composition boundary.
|
|
33
|
+
*/
|
|
34
|
+
export async function createHabitatClientForWorkspace(workspaceRoot) {
|
|
35
|
+
const ready = await deps;
|
|
36
|
+
const resolvedWorkspaceRoot = ready.path.resolve(workspaceRoot);
|
|
37
|
+
const workspaceRequire = createRequire(ready.path.join(resolvedWorkspaceRoot, "package.json"));
|
|
38
|
+
const packageJsonPath = workspaceRequire.resolve(`${HABITAT_BLUEPRINT_PACK}/package.json`);
|
|
39
|
+
return createClient({
|
|
40
|
+
deps: ready,
|
|
41
|
+
scope: { workspaceRoot: resolvedWorkspaceRoot },
|
|
42
|
+
config: {
|
|
43
|
+
policyPack: {
|
|
44
|
+
name: HABITAT_BLUEPRINT_PACK,
|
|
45
|
+
packageJsonPath,
|
|
46
|
+
manifestPath: ready.path.join(ready.path.dirname(packageJsonPath), "habitat-pack.json"),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function decodeCommandTimeout(input) {
|
|
52
|
+
if (input === undefined)
|
|
53
|
+
return 30_000;
|
|
54
|
+
const value = Number(input);
|
|
55
|
+
if (!commandTimeoutValidator.Check(value)) {
|
|
56
|
+
throw new Error("HABITAT_COMMAND_TIMEOUT_MS must be an integer from 1 through 600000.");
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/generators/init.ts
|
|
40
|
+
var exports_init = {};
|
|
41
|
+
__export(exports_init, {
|
|
42
|
+
default: () => initializeHabitat
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(exports_init);
|
|
45
|
+
var import_devkit2 = require("@nx/devkit");
|
|
46
|
+
|
|
47
|
+
// ../../plugins/nx/habitat/src/initialization.ts
|
|
48
|
+
var import_node_util = require("node:util");
|
|
49
|
+
var import_devkit = require("@nx/devkit");
|
|
50
|
+
var import_typebox = require("typebox");
|
|
51
|
+
var import_schema = require("typebox/schema");
|
|
52
|
+
var PACKAGE_PATH = "package.json";
|
|
53
|
+
var CODEX_HOOKS_PATH = ".codex/hooks.json";
|
|
54
|
+
var HabitatHookMarkerSchema = import_typebox.Type.Object({
|
|
55
|
+
identity: import_typebox.Type.String({
|
|
56
|
+
minLength: 1,
|
|
57
|
+
description: "Stable package-owned identity for one Habitat hook contribution."
|
|
58
|
+
}),
|
|
59
|
+
revision: import_typebox.Type.Integer({
|
|
60
|
+
minimum: 0,
|
|
61
|
+
description: "Monotonic payload revision for the named Habitat contribution."
|
|
62
|
+
})
|
|
63
|
+
}, { additionalProperties: false, description: "Habitat hook ownership marker." });
|
|
64
|
+
var HookGroupSchema = import_typebox.Type.Object({
|
|
65
|
+
_habitat: import_typebox.Type.Optional(HabitatHookMarkerSchema),
|
|
66
|
+
hooks: import_typebox.Type.Array(import_typebox.Type.Unknown(), {
|
|
67
|
+
description: "Ordered command contributions in one Codex hook group."
|
|
68
|
+
})
|
|
69
|
+
}, { additionalProperties: true, description: "One consumer-owned Codex hook group." });
|
|
70
|
+
var HabitatHookCommandSchema = import_typebox.Type.Object({
|
|
71
|
+
type: import_typebox.Type.Literal("command", {
|
|
72
|
+
description: "Codex hook handler kind owned by the Habitat initializer."
|
|
73
|
+
}),
|
|
74
|
+
command: import_typebox.Type.String({
|
|
75
|
+
minLength: 1,
|
|
76
|
+
description: "Installed Habitat command executed by the Codex hook."
|
|
77
|
+
}),
|
|
78
|
+
statusMessage: import_typebox.Type.String({
|
|
79
|
+
minLength: 1,
|
|
80
|
+
description: "Operator-facing status rendered while Habitat checks run."
|
|
81
|
+
}),
|
|
82
|
+
timeout: import_typebox.Type.Integer({
|
|
83
|
+
minimum: 1,
|
|
84
|
+
description: "Maximum seconds allowed for the Habitat hook command."
|
|
85
|
+
})
|
|
86
|
+
}, { additionalProperties: false, description: "One Habitat-owned Codex hook command." });
|
|
87
|
+
var HabitatOwnedHookGroupSchema = import_typebox.Type.Object({
|
|
88
|
+
_habitat: HabitatHookMarkerSchema,
|
|
89
|
+
hooks: import_typebox.Type.Array(HabitatHookCommandSchema, {
|
|
90
|
+
minItems: 1,
|
|
91
|
+
description: "Commands contributed by the installed Habitat package."
|
|
92
|
+
})
|
|
93
|
+
}, { additionalProperties: false, description: "The named Habitat Codex hook contribution." });
|
|
94
|
+
var CodexHooksSchema = import_typebox.Type.Object({
|
|
95
|
+
hooks: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Array(HookGroupSchema), {
|
|
96
|
+
description: "Ordered hook groups keyed by Codex event identity."
|
|
97
|
+
}))
|
|
98
|
+
}, { additionalProperties: true, description: "Consumer-owned Codex hook configuration." });
|
|
99
|
+
var ConsumerPackageSchema = import_typebox.Type.Object({
|
|
100
|
+
trustedDependencies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String({ minLength: 1 }), {
|
|
101
|
+
description: "Package lifecycle scripts explicitly trusted by the Bun consumer."
|
|
102
|
+
}))
|
|
103
|
+
}, { additionalProperties: true, description: "Nx consumer package metadata." });
|
|
104
|
+
var hooksValidator = new import_schema.Validator({}, CodexHooksSchema);
|
|
105
|
+
var packageValidator = new import_schema.Validator({}, ConsumerPackageSchema);
|
|
106
|
+
function initializeHabitatConsumer(tree, binding) {
|
|
107
|
+
const nxJson = requireNxJson(tree);
|
|
108
|
+
const hooks = readHooks(tree);
|
|
109
|
+
const packageJson = readPackage(tree);
|
|
110
|
+
const nxPlan = planNxInitialization(nxJson, binding);
|
|
111
|
+
const hookPlan = planHookInitialization(hooks, binding);
|
|
112
|
+
const packagePlan = planPackageInitialization(packageJson, binding.gritPackage);
|
|
113
|
+
if (nxPlan.changed)
|
|
114
|
+
import_devkit.updateNxJson(tree, nxPlan.value);
|
|
115
|
+
if (hookPlan.changed)
|
|
116
|
+
import_devkit.writeJson(tree, CODEX_HOOKS_PATH, hookPlan.value);
|
|
117
|
+
if (packagePlan.changed)
|
|
118
|
+
import_devkit.writeJson(tree, PACKAGE_PATH, packagePlan.value);
|
|
119
|
+
return { packageChanged: packagePlan.changed };
|
|
120
|
+
}
|
|
121
|
+
function requireNxJson(tree) {
|
|
122
|
+
const nxJson = import_devkit.readNxJson(tree);
|
|
123
|
+
if (nxJson === null) {
|
|
124
|
+
throw new Error("Habitat initialization requires an Nx workspace with nx.json.");
|
|
125
|
+
}
|
|
126
|
+
return nxJson;
|
|
127
|
+
}
|
|
128
|
+
function readHooks(tree) {
|
|
129
|
+
if (!tree.exists(CODEX_HOOKS_PATH))
|
|
130
|
+
return { hooks: {} };
|
|
131
|
+
const input = import_devkit.readJson(tree, CODEX_HOOKS_PATH);
|
|
132
|
+
if (!hooksValidator.Check(input)) {
|
|
133
|
+
throw new Error(`${CODEX_HOOKS_PATH} is not a supported Codex hook document.`);
|
|
134
|
+
}
|
|
135
|
+
return input;
|
|
136
|
+
}
|
|
137
|
+
function readPackage(tree) {
|
|
138
|
+
if (!tree.exists(PACKAGE_PATH)) {
|
|
139
|
+
throw new Error("Habitat initialization requires an Nx workspace package.json.");
|
|
140
|
+
}
|
|
141
|
+
const input = import_devkit.readJson(tree, PACKAGE_PATH);
|
|
142
|
+
if (!packageValidator.Check(input)) {
|
|
143
|
+
throw new Error("package.json is not a supported Nx consumer package document.");
|
|
144
|
+
}
|
|
145
|
+
return input;
|
|
146
|
+
}
|
|
147
|
+
function planNxInitialization(nxJson, binding) {
|
|
148
|
+
const plugins = nxJson.plugins ?? [];
|
|
149
|
+
const matches = plugins.filter((plugin) => hasPluginIdentity(plugin, binding.nxPlugin) || binding.predecessorNxPlugins.some((predecessor) => hasPluginIdentity(plugin, typeof predecessor === "string" ? predecessor : predecessor.plugin)));
|
|
150
|
+
if (matches.length > 1) {
|
|
151
|
+
throw new Error("nx.json contains multiple Habitat Nx plugin registrations.");
|
|
152
|
+
}
|
|
153
|
+
const match = matches[0];
|
|
154
|
+
if (match === binding.nxPlugin)
|
|
155
|
+
return { changed: false, value: nxJson };
|
|
156
|
+
if (match !== undefined && !binding.predecessorNxPlugins.some((predecessor) => import_node_util.isDeepStrictEqual(predecessor, match))) {
|
|
157
|
+
throw new Error("nx.json contains an incompatible Habitat Nx plugin registration.");
|
|
158
|
+
}
|
|
159
|
+
const nextPlugins = match === undefined ? [...plugins, binding.nxPlugin] : plugins.map((plugin) => plugin === match ? binding.nxPlugin : plugin);
|
|
160
|
+
return { changed: true, value: { ...nxJson, plugins: nextPlugins } };
|
|
161
|
+
}
|
|
162
|
+
function planHookInitialization(hooks, binding) {
|
|
163
|
+
const location = oneOwnedHookLocation(hooks, binding);
|
|
164
|
+
if (location !== undefined && import_node_util.isDeepStrictEqual(location.group, binding.hook)) {
|
|
165
|
+
return { changed: false, value: hooks };
|
|
166
|
+
}
|
|
167
|
+
if (location !== undefined && !binding.predecessorHooks.some((predecessor) => import_node_util.isDeepStrictEqual(predecessor, location.group))) {
|
|
168
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains an incompatible Habitat hook contribution.`);
|
|
169
|
+
}
|
|
170
|
+
const events = hooks.hooks ?? {};
|
|
171
|
+
const stop = events.Stop ?? [];
|
|
172
|
+
const nextStop = location === undefined ? [...stop, binding.hook] : stop.map((group, index) => index === location.index ? binding.hook : group);
|
|
173
|
+
return {
|
|
174
|
+
changed: true,
|
|
175
|
+
value: { ...hooks, hooks: { ...events, Stop: nextStop } }
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function planPackageInitialization(packageJson, gritPackage) {
|
|
179
|
+
const trusted = packageJson.trustedDependencies ?? [];
|
|
180
|
+
const matches = trusted.filter((dependency) => dependency === gritPackage);
|
|
181
|
+
if (matches.length > 1) {
|
|
182
|
+
throw new Error(`package.json contains duplicate ${gritPackage} trust entries.`);
|
|
183
|
+
}
|
|
184
|
+
if (matches.length === 1)
|
|
185
|
+
return { changed: false, value: packageJson };
|
|
186
|
+
return {
|
|
187
|
+
changed: true,
|
|
188
|
+
value: { ...packageJson, trustedDependencies: [...trusted, gritPackage] }
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function oneOwnedHookLocation(hooks, binding) {
|
|
192
|
+
const identity = binding.hook._habitat.identity;
|
|
193
|
+
const locations = Object.entries(hooks.hooks ?? {}).flatMap(([event, groups]) => groups.flatMap((group, index) => {
|
|
194
|
+
const marked = group._habitat?.identity === identity;
|
|
195
|
+
const predecessor = binding.predecessorHooks.some((candidate) => import_node_util.isDeepStrictEqual(candidate, group));
|
|
196
|
+
return marked || predecessor ? [{ event, group, index }] : [];
|
|
197
|
+
}));
|
|
198
|
+
if (locations.length > 1) {
|
|
199
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains multiple Habitat hook contributions.`);
|
|
200
|
+
}
|
|
201
|
+
const location = locations[0];
|
|
202
|
+
if (location !== undefined && location.event !== "Stop") {
|
|
203
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains a Habitat hook contribution outside Stop.`);
|
|
204
|
+
}
|
|
205
|
+
return location;
|
|
206
|
+
}
|
|
207
|
+
function hasPluginIdentity(plugin, identity) {
|
|
208
|
+
return plugin === identity || typeof plugin === "object" && plugin.plugin === identity;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/nx-generators.ts
|
|
212
|
+
var habitatConsumerBinding = {
|
|
213
|
+
gritPackage: "@getgrit/cli",
|
|
214
|
+
nxPlugin: "@habitat-ai/cli/nx-plugin",
|
|
215
|
+
predecessorNxPlugins: [
|
|
216
|
+
{
|
|
217
|
+
plugin: "@habitat/cli/nx-plugin",
|
|
218
|
+
options: { checkTargetName: "check:policy" }
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
hook: {
|
|
222
|
+
_habitat: { identity: "@habitat-ai/cli:agent-stop", revision: 1 },
|
|
223
|
+
hooks: [
|
|
224
|
+
{
|
|
225
|
+
type: "command",
|
|
226
|
+
command: 'bash -lc \'repo="${CODEX_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-}}"; if [ -n "$repo" ]; then repo="$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null)"; else repo="$(git rev-parse --show-toplevel 2>/dev/null)"; fi && cd "$repo" 2>/dev/null || { printf "%s\\n" "Habitat agent-stop hook must run inside the repository worktree." >&2; exit 2; }; bunx --bun --no-install --package @habitat-ai/cli habitat hook agent-stop\'',
|
|
227
|
+
timeout: 120,
|
|
228
|
+
statusMessage: "Checking Habitat structure laws"
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
predecessorHooks: [
|
|
233
|
+
{
|
|
234
|
+
hooks: [
|
|
235
|
+
{
|
|
236
|
+
type: "command",
|
|
237
|
+
command: 'bash -lc \'repo="${CODEX_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-}}"; if [ -n "$repo" ]; then repo="$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null)"; else repo="$(git rev-parse --show-toplevel 2>/dev/null)"; fi && cd "$repo" 2>/dev/null || { printf "%s\\n" "Habitat agent-stop hook must run inside the repository worktree." >&2; exit 2; }; bun habitat hook agent-stop\'',
|
|
238
|
+
timeout: 120,
|
|
239
|
+
statusMessage: "Checking Habitat structure laws"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/generators/init.ts
|
|
247
|
+
function initializeHabitat(tree) {
|
|
248
|
+
const result = initializeHabitatConsumer(tree, habitatConsumerBinding);
|
|
249
|
+
if (!result.packageChanged)
|
|
250
|
+
return;
|
|
251
|
+
return () => import_devkit2.installPackagesTask(tree);
|
|
252
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/generators/remove-hook.ts
|
|
40
|
+
var exports_remove_hook = {};
|
|
41
|
+
__export(exports_remove_hook, {
|
|
42
|
+
default: () => removeHook
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(exports_remove_hook);
|
|
45
|
+
|
|
46
|
+
// ../../plugins/nx/habitat/src/initialization.ts
|
|
47
|
+
var import_node_util = require("node:util");
|
|
48
|
+
var import_devkit = require("@nx/devkit");
|
|
49
|
+
var import_typebox = require("typebox");
|
|
50
|
+
var import_schema = require("typebox/schema");
|
|
51
|
+
var CODEX_HOOKS_PATH = ".codex/hooks.json";
|
|
52
|
+
var HabitatHookMarkerSchema = import_typebox.Type.Object({
|
|
53
|
+
identity: import_typebox.Type.String({
|
|
54
|
+
minLength: 1,
|
|
55
|
+
description: "Stable package-owned identity for one Habitat hook contribution."
|
|
56
|
+
}),
|
|
57
|
+
revision: import_typebox.Type.Integer({
|
|
58
|
+
minimum: 0,
|
|
59
|
+
description: "Monotonic payload revision for the named Habitat contribution."
|
|
60
|
+
})
|
|
61
|
+
}, { additionalProperties: false, description: "Habitat hook ownership marker." });
|
|
62
|
+
var HookGroupSchema = import_typebox.Type.Object({
|
|
63
|
+
_habitat: import_typebox.Type.Optional(HabitatHookMarkerSchema),
|
|
64
|
+
hooks: import_typebox.Type.Array(import_typebox.Type.Unknown(), {
|
|
65
|
+
description: "Ordered command contributions in one Codex hook group."
|
|
66
|
+
})
|
|
67
|
+
}, { additionalProperties: true, description: "One consumer-owned Codex hook group." });
|
|
68
|
+
var HabitatHookCommandSchema = import_typebox.Type.Object({
|
|
69
|
+
type: import_typebox.Type.Literal("command", {
|
|
70
|
+
description: "Codex hook handler kind owned by the Habitat initializer."
|
|
71
|
+
}),
|
|
72
|
+
command: import_typebox.Type.String({
|
|
73
|
+
minLength: 1,
|
|
74
|
+
description: "Installed Habitat command executed by the Codex hook."
|
|
75
|
+
}),
|
|
76
|
+
statusMessage: import_typebox.Type.String({
|
|
77
|
+
minLength: 1,
|
|
78
|
+
description: "Operator-facing status rendered while Habitat checks run."
|
|
79
|
+
}),
|
|
80
|
+
timeout: import_typebox.Type.Integer({
|
|
81
|
+
minimum: 1,
|
|
82
|
+
description: "Maximum seconds allowed for the Habitat hook command."
|
|
83
|
+
})
|
|
84
|
+
}, { additionalProperties: false, description: "One Habitat-owned Codex hook command." });
|
|
85
|
+
var HabitatOwnedHookGroupSchema = import_typebox.Type.Object({
|
|
86
|
+
_habitat: HabitatHookMarkerSchema,
|
|
87
|
+
hooks: import_typebox.Type.Array(HabitatHookCommandSchema, {
|
|
88
|
+
minItems: 1,
|
|
89
|
+
description: "Commands contributed by the installed Habitat package."
|
|
90
|
+
})
|
|
91
|
+
}, { additionalProperties: false, description: "The named Habitat Codex hook contribution." });
|
|
92
|
+
var CodexHooksSchema = import_typebox.Type.Object({
|
|
93
|
+
hooks: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Array(HookGroupSchema), {
|
|
94
|
+
description: "Ordered hook groups keyed by Codex event identity."
|
|
95
|
+
}))
|
|
96
|
+
}, { additionalProperties: true, description: "Consumer-owned Codex hook configuration." });
|
|
97
|
+
var ConsumerPackageSchema = import_typebox.Type.Object({
|
|
98
|
+
trustedDependencies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String({ minLength: 1 }), {
|
|
99
|
+
description: "Package lifecycle scripts explicitly trusted by the Bun consumer."
|
|
100
|
+
}))
|
|
101
|
+
}, { additionalProperties: true, description: "Nx consumer package metadata." });
|
|
102
|
+
var hooksValidator = new import_schema.Validator({}, CodexHooksSchema);
|
|
103
|
+
var packageValidator = new import_schema.Validator({}, ConsumerPackageSchema);
|
|
104
|
+
function removeHabitatHook(tree, binding) {
|
|
105
|
+
const hooks = readHooks(tree);
|
|
106
|
+
const hookPlan = planHookRemoval(hooks, binding);
|
|
107
|
+
if (hookPlan.changed)
|
|
108
|
+
import_devkit.writeJson(tree, CODEX_HOOKS_PATH, hookPlan.value);
|
|
109
|
+
}
|
|
110
|
+
function readHooks(tree) {
|
|
111
|
+
if (!tree.exists(CODEX_HOOKS_PATH))
|
|
112
|
+
return { hooks: {} };
|
|
113
|
+
const input = import_devkit.readJson(tree, CODEX_HOOKS_PATH);
|
|
114
|
+
if (!hooksValidator.Check(input)) {
|
|
115
|
+
throw new Error(`${CODEX_HOOKS_PATH} is not a supported Codex hook document.`);
|
|
116
|
+
}
|
|
117
|
+
return input;
|
|
118
|
+
}
|
|
119
|
+
function planHookRemoval(hooks, binding) {
|
|
120
|
+
const location = oneOwnedHookLocation(hooks, binding);
|
|
121
|
+
if (location === undefined)
|
|
122
|
+
return { changed: false, value: hooks };
|
|
123
|
+
if (!import_node_util.isDeepStrictEqual(location.group, binding.hook) && !binding.predecessorHooks.some((predecessor) => import_node_util.isDeepStrictEqual(predecessor, location.group))) {
|
|
124
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains an incompatible Habitat hook contribution.`);
|
|
125
|
+
}
|
|
126
|
+
const events = hooks.hooks ?? {};
|
|
127
|
+
const stop = events.Stop ?? [];
|
|
128
|
+
return {
|
|
129
|
+
changed: true,
|
|
130
|
+
value: {
|
|
131
|
+
...hooks,
|
|
132
|
+
hooks: { ...events, Stop: stop.filter((_group, index) => index !== location.index) }
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function oneOwnedHookLocation(hooks, binding) {
|
|
137
|
+
const identity = binding.hook._habitat.identity;
|
|
138
|
+
const locations = Object.entries(hooks.hooks ?? {}).flatMap(([event, groups]) => groups.flatMap((group, index) => {
|
|
139
|
+
const marked = group._habitat?.identity === identity;
|
|
140
|
+
const predecessor = binding.predecessorHooks.some((candidate) => import_node_util.isDeepStrictEqual(candidate, group));
|
|
141
|
+
return marked || predecessor ? [{ event, group, index }] : [];
|
|
142
|
+
}));
|
|
143
|
+
if (locations.length > 1) {
|
|
144
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains multiple Habitat hook contributions.`);
|
|
145
|
+
}
|
|
146
|
+
const location = locations[0];
|
|
147
|
+
if (location !== undefined && location.event !== "Stop") {
|
|
148
|
+
throw new Error(`${CODEX_HOOKS_PATH} contains a Habitat hook contribution outside Stop.`);
|
|
149
|
+
}
|
|
150
|
+
return location;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/nx-generators.ts
|
|
154
|
+
var habitatConsumerBinding = {
|
|
155
|
+
gritPackage: "@getgrit/cli",
|
|
156
|
+
nxPlugin: "@habitat-ai/cli/nx-plugin",
|
|
157
|
+
predecessorNxPlugins: [
|
|
158
|
+
{
|
|
159
|
+
plugin: "@habitat/cli/nx-plugin",
|
|
160
|
+
options: { checkTargetName: "check:policy" }
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
hook: {
|
|
164
|
+
_habitat: { identity: "@habitat-ai/cli:agent-stop", revision: 1 },
|
|
165
|
+
hooks: [
|
|
166
|
+
{
|
|
167
|
+
type: "command",
|
|
168
|
+
command: 'bash -lc \'repo="${CODEX_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-}}"; if [ -n "$repo" ]; then repo="$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null)"; else repo="$(git rev-parse --show-toplevel 2>/dev/null)"; fi && cd "$repo" 2>/dev/null || { printf "%s\\n" "Habitat agent-stop hook must run inside the repository worktree." >&2; exit 2; }; bunx --bun --no-install --package @habitat-ai/cli habitat hook agent-stop\'',
|
|
169
|
+
timeout: 120,
|
|
170
|
+
statusMessage: "Checking Habitat structure laws"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
predecessorHooks: [
|
|
175
|
+
{
|
|
176
|
+
hooks: [
|
|
177
|
+
{
|
|
178
|
+
type: "command",
|
|
179
|
+
command: 'bash -lc \'repo="${CODEX_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-}}"; if [ -n "$repo" ]; then repo="$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null)"; else repo="$(git rev-parse --show-toplevel 2>/dev/null)"; fi && cd "$repo" 2>/dev/null || { printf "%s\\n" "Habitat agent-stop hook must run inside the repository worktree." >&2; exit 2; }; bun habitat hook agent-stop\'',
|
|
180
|
+
timeout: 120,
|
|
181
|
+
statusMessage: "Checking Habitat structure laws"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// src/generators/remove-hook.ts
|
|
189
|
+
function removeHook(tree) {
|
|
190
|
+
removeHabitatHook(tree, habitatConsumerBinding);
|
|
191
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|