@gitgov/core 2.8.0 → 2.9.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/dist/src/index.js CHANGED
@@ -28,6 +28,25 @@ __export(adapters_exports, {
28
28
  });
29
29
 
30
30
  // src/record_types/common.types.ts
31
+ var RECORD_TYPES = [
32
+ "actor",
33
+ "agent",
34
+ "cycle",
35
+ "task",
36
+ "execution",
37
+ "feedback"
38
+ ];
39
+ var DIR_TO_TYPE = {
40
+ "tasks": "task",
41
+ "cycles": "cycle",
42
+ "executions": "execution",
43
+ "feedbacks": "feedback",
44
+ "actors": "actor",
45
+ "agents": "agent"
46
+ };
47
+ var TYPE_TO_DIR = Object.fromEntries(
48
+ Object.entries(DIR_TO_TYPE).map(([dir, type]) => [type, dir])
49
+ );
31
50
  var GitGovError = class extends Error {
32
51
  constructor(message, code) {
33
52
  super(message);
@@ -921,6 +940,7 @@ var embedded_metadata_schema_default = {
921
940
  if: {
922
941
  properties: {
923
942
  header: {
943
+ type: "object",
924
944
  properties: {
925
945
  type: {
926
946
  const: "custom"
@@ -945,6 +965,7 @@ var embedded_metadata_schema_default = {
945
965
  if: {
946
966
  properties: {
947
967
  header: {
968
+ type: "object",
948
969
  properties: {
949
970
  type: {
950
971
  const: "actor"
@@ -965,6 +986,7 @@ var embedded_metadata_schema_default = {
965
986
  if: {
966
987
  properties: {
967
988
  header: {
989
+ type: "object",
968
990
  properties: {
969
991
  type: {
970
992
  const: "agent"
@@ -985,6 +1007,7 @@ var embedded_metadata_schema_default = {
985
1007
  if: {
986
1008
  properties: {
987
1009
  header: {
1010
+ type: "object",
988
1011
  properties: {
989
1012
  type: {
990
1013
  const: "task"
@@ -1005,6 +1028,7 @@ var embedded_metadata_schema_default = {
1005
1028
  if: {
1006
1029
  properties: {
1007
1030
  header: {
1031
+ type: "object",
1008
1032
  properties: {
1009
1033
  type: {
1010
1034
  const: "execution"
@@ -1025,6 +1049,7 @@ var embedded_metadata_schema_default = {
1025
1049
  if: {
1026
1050
  properties: {
1027
1051
  header: {
1052
+ type: "object",
1028
1053
  properties: {
1029
1054
  type: {
1030
1055
  const: "feedback"
@@ -1045,6 +1070,7 @@ var embedded_metadata_schema_default = {
1045
1070
  if: {
1046
1071
  properties: {
1047
1072
  header: {
1073
+ type: "object",
1048
1074
  properties: {
1049
1075
  type: {
1050
1076
  const: "cycle"
@@ -1065,6 +1091,7 @@ var embedded_metadata_schema_default = {
1065
1091
  if: {
1066
1092
  properties: {
1067
1093
  header: {
1094
+ type: "object",
1068
1095
  properties: {
1069
1096
  type: {
1070
1097
  const: "workflow"
@@ -6742,6 +6769,7 @@ var FsFileLister = class {
6742
6769
  );
6743
6770
  }
6744
6771
  }
6772
+ const normalized = patterns.map((p) => p.endsWith("/") ? `${p}**` : p);
6745
6773
  const fgOptions = {
6746
6774
  cwd: this.cwd,
6747
6775
  ignore: options?.ignore ?? [],
@@ -6752,7 +6780,7 @@ var FsFileLister = class {
6752
6780
  if (options?.maxDepth !== void 0) {
6753
6781
  fgOptions.deep = options.maxDepth;
6754
6782
  }
6755
- return fg(patterns, fgOptions);
6783
+ return fg(normalized, fgOptions);
6756
6784
  }
6757
6785
  /**
6758
6786
  * [EARS-FL02] Checks if a file exists.
@@ -8056,6 +8084,220 @@ function isRebaseAlreadyInProgressError(error) {
8056
8084
  return error instanceof RebaseAlreadyInProgressError;
8057
8085
  }
8058
8086
 
8087
+ // src/hook_handler/index.ts
8088
+ var hook_handler_exports = {};
8089
+ __export(hook_handler_exports, {
8090
+ HookHandler: () => HookHandler,
8091
+ classifyCommand: () => classifyCommand
8092
+ });
8093
+
8094
+ // src/hook_handler/hook_handler.ts
8095
+ var HookHandler = class {
8096
+ executionAdapter;
8097
+ sessionManager;
8098
+ configManager;
8099
+ constructor(deps) {
8100
+ this.executionAdapter = deps.executionAdapter;
8101
+ this.sessionManager = deps.sessionManager;
8102
+ this.configManager = deps.configManager;
8103
+ }
8104
+ /**
8105
+ * Process a hook event and decide whether to create an ExecutionRecord.
8106
+ * Fail-silent: catches all errors and returns { action: 'skipped', reason }.
8107
+ */
8108
+ async handleEvent(event, options = {}) {
8109
+ const dryRun = options.dryRun ?? false;
8110
+ try {
8111
+ const config = await this.configManager.loadConfig();
8112
+ if (!config) {
8113
+ return { action: "skipped", reason: "no config" };
8114
+ }
8115
+ if (isFileChangedEvent(event)) {
8116
+ return { action: "skipped", reason: "file changes are not recorded" };
8117
+ }
8118
+ if (isTeammateIdleEvent(event)) {
8119
+ return { action: "skipped", reason: "activity logged" };
8120
+ }
8121
+ const actorId = await this.resolveActorId();
8122
+ const activeTaskId = actorId ? (await this.sessionManager.getActorState(actorId))?.activeTaskId ?? null : null;
8123
+ if (isSessionEndEvent(event)) {
8124
+ return await this.handleSessionEnd(event, actorId, activeTaskId, dryRun);
8125
+ }
8126
+ if (!activeTaskId) {
8127
+ return { action: "skipped", reason: "no active task" };
8128
+ }
8129
+ if (isCommandExecutedEvent(event)) {
8130
+ return await this.handleCommandExecuted(event, actorId, activeTaskId, dryRun);
8131
+ }
8132
+ if (isTaskCompletedEvent(event)) {
8133
+ return await this.handleTaskCompleted(event, actorId, activeTaskId, dryRun);
8134
+ }
8135
+ return { action: "skipped", reason: "unknown event type" };
8136
+ } catch (error) {
8137
+ const message = error instanceof Error ? error.message : String(error);
8138
+ return { action: "skipped", reason: message };
8139
+ }
8140
+ }
8141
+ // ─── Command Executed ──────────────────────────────────────
8142
+ async handleCommandExecuted(event, actorId, activeTaskId, dryRun = false) {
8143
+ if (event.exit_code !== 0) {
8144
+ return { action: "skipped", reason: "command failed" };
8145
+ }
8146
+ const classification = classifyCommand(event.tool_input.command, event.tool_output);
8147
+ if (classification.kind === "unknown") {
8148
+ return { action: "skipped", reason: "unrecognized command" };
8149
+ }
8150
+ const payload = this.buildCommandPayload(classification, activeTaskId);
8151
+ if (dryRun) {
8152
+ return { action: "recorded", executionId: "dry-run" };
8153
+ }
8154
+ const record = await this.executionAdapter.create(payload, actorId);
8155
+ return { action: "recorded", executionId: record.id };
8156
+ }
8157
+ buildCommandPayload(classification, activeTaskId) {
8158
+ switch (classification.kind) {
8159
+ // [EARS-B2]
8160
+ case "commit":
8161
+ return {
8162
+ taskId: activeTaskId,
8163
+ type: "completion",
8164
+ title: `Commit ${classification.hash}`,
8165
+ result: `Commit ${classification.hash}: ${classification.message} (${classification.filesChanged} files changed)`,
8166
+ references: [`commit:${classification.hash}`]
8167
+ };
8168
+ // [EARS-B3]
8169
+ case "pr":
8170
+ return {
8171
+ taskId: activeTaskId,
8172
+ type: "completion",
8173
+ title: `PR #${classification.number} created`,
8174
+ result: `PR #${classification.number} created`,
8175
+ references: [`pr:${classification.number}`]
8176
+ };
8177
+ // [EARS-B4]
8178
+ case "test":
8179
+ return {
8180
+ taskId: activeTaskId,
8181
+ type: "analysis",
8182
+ title: "Test run",
8183
+ result: `Tests: ${classification.passed}/${classification.total} passing, ${classification.failed} failed`,
8184
+ metadata: { tests: { passed: classification.passed, failed: classification.failed, total: classification.total } }
8185
+ };
8186
+ }
8187
+ }
8188
+ // ─── Task Completed ────────────────────────────────────────
8189
+ async handleTaskCompleted(event, actorId, activeTaskId, dryRun = false) {
8190
+ const payload = {
8191
+ taskId: activeTaskId,
8192
+ type: "completion",
8193
+ title: `Task completed: ${event.task.subject}`,
8194
+ result: `Task "${event.task.subject}" completed${event.task.owner ? ` by ${event.task.owner}` : ""}`,
8195
+ references: [`task:${event.task.id}`]
8196
+ };
8197
+ if (dryRun) {
8198
+ return { action: "recorded", executionId: "dry-run" };
8199
+ }
8200
+ const record = await this.executionAdapter.create(payload, actorId);
8201
+ return { action: "recorded", executionId: record.id };
8202
+ }
8203
+ // ─── Session End ───────────────────────────────────────────
8204
+ async handleSessionEnd(event, actorId, activeTaskId, dryRun = false) {
8205
+ if (!actorId) {
8206
+ return { action: "skipped", reason: "no actor" };
8207
+ }
8208
+ if (!activeTaskId) {
8209
+ return { action: "skipped", reason: "no active task" };
8210
+ }
8211
+ const taskId = activeTaskId;
8212
+ const payload = {
8213
+ taskId,
8214
+ type: "analysis",
8215
+ title: "Session ended",
8216
+ result: `Session ended${event.session_id ? ` (${event.session_id})` : ""}`
8217
+ };
8218
+ if (dryRun) {
8219
+ return { action: "recorded", executionId: "dry-run" };
8220
+ }
8221
+ const record = await this.executionAdapter.create(payload, actorId);
8222
+ return { action: "recorded", executionId: record.id };
8223
+ }
8224
+ // ─── Helpers ───────────────────────────────────────────────
8225
+ async resolveActorId() {
8226
+ const lastSession = await this.sessionManager.getLastSession();
8227
+ if (lastSession) return lastSession.actorId;
8228
+ const detectedId = await this.sessionManager.detectActorFromKeyFiles();
8229
+ return detectedId;
8230
+ }
8231
+ };
8232
+ function isCommandExecutedEvent(event) {
8233
+ return "tool_name" in event && event.tool_name === "Bash";
8234
+ }
8235
+ function isFileChangedEvent(event) {
8236
+ return "tool_name" in event && (event.tool_name === "Write" || event.tool_name === "Edit");
8237
+ }
8238
+ function isTaskCompletedEvent(event) {
8239
+ return "hook_type" in event && event.hook_type === "TaskCompleted";
8240
+ }
8241
+ function isTeammateIdleEvent(event) {
8242
+ return "hook_type" in event && event.hook_type === "TeammateIdle";
8243
+ }
8244
+ function isSessionEndEvent(event) {
8245
+ return "hook_type" in event && event.hook_type === "Stop";
8246
+ }
8247
+ function classifyCommand(command, output) {
8248
+ if (/git\s+commit/.test(command)) {
8249
+ return parseCommitOutput(output);
8250
+ }
8251
+ if (/gh\s+pr\s+create/.test(command)) {
8252
+ return parsePrOutput(output);
8253
+ }
8254
+ if (/(?:jest|vitest|pytest|npm\s+test|pnpm\s+test|npx\s+vitest|npx\s+jest)/.test(command)) {
8255
+ return parseTestOutput(output);
8256
+ }
8257
+ return { kind: "unknown" };
8258
+ }
8259
+ function parseCommitOutput(output) {
8260
+ if (!output) return { kind: "commit", hash: "unknown", message: "", filesChanged: 0 };
8261
+ const hashMatch = output.match(/\[[\w/.-]+\s+([a-f0-9]+)\]/);
8262
+ const hash = hashMatch?.[1] ?? "unknown";
8263
+ const messageMatch = output.match(/\]\s+(.+?)(?:\n|$)/);
8264
+ const message = messageMatch?.[1] ?? "";
8265
+ const filesMatch = output.match(/(\d+)\s+files?\s+changed/);
8266
+ const filesChanged = filesMatch?.[1] ? parseInt(filesMatch[1], 10) : 0;
8267
+ return { kind: "commit", hash, message, filesChanged };
8268
+ }
8269
+ function parsePrOutput(output) {
8270
+ if (!output) return { kind: "pr", number: "unknown" };
8271
+ const prMatch = output.match(/\/pull\/(\d+)/);
8272
+ if (prMatch?.[1]) return { kind: "pr", number: prMatch[1] };
8273
+ const numMatch = output.match(/#(\d+)/);
8274
+ return { kind: "pr", number: numMatch?.[1] ?? "unknown" };
8275
+ }
8276
+ function parseTestOutput(output) {
8277
+ if (!output) return { kind: "test", passed: 0, failed: 0, total: 0 };
8278
+ let passed = 0;
8279
+ let failed = 0;
8280
+ let total = 0;
8281
+ const vitestMatch = output.match(/Tests?\s+(\d+)\s+passed/);
8282
+ const failedMatch = output.match(/(\d+)\s+failed/);
8283
+ const totalMatch = output.match(/(\d+)\s+total/);
8284
+ if (vitestMatch?.[1]) passed = parseInt(vitestMatch[1], 10);
8285
+ if (failedMatch?.[1]) failed = parseInt(failedMatch[1], 10);
8286
+ if (totalMatch?.[1]) {
8287
+ total = parseInt(totalMatch[1], 10);
8288
+ } else {
8289
+ total = passed + failed;
8290
+ }
8291
+ if (!vitestMatch) {
8292
+ const pytestPassed = output.match(/(\d+)\s+passed/);
8293
+ const pytestFailed = output.match(/(\d+)\s+failed/);
8294
+ if (pytestPassed?.[1]) passed = parseInt(pytestPassed[1], 10);
8295
+ if (pytestFailed?.[1]) failed = parseInt(pytestFailed[1], 10);
8296
+ total = passed + failed;
8297
+ }
8298
+ return { kind: "test", passed, failed, total };
8299
+ }
8300
+
8059
8301
  // src/record_validations/index.ts
8060
8302
  var record_validations_exports = {};
8061
8303
  __export(record_validations_exports, {
@@ -8097,7 +8339,10 @@ __export(record_validations_exports, {
8097
8339
  // src/record_types/index.ts
8098
8340
  var record_types_exports = {};
8099
8341
  __export(record_types_exports, {
8100
- GitGovError: () => GitGovError
8342
+ DIR_TO_TYPE: () => DIR_TO_TYPE,
8343
+ GitGovError: () => GitGovError,
8344
+ RECORD_TYPES: () => RECORD_TYPES,
8345
+ TYPE_TO_DIR: () => TYPE_TO_DIR
8101
8346
  });
8102
8347
 
8103
8348
  // src/event_bus/index.ts
@@ -11044,6 +11289,6 @@ var RecordProjector = class {
11044
11289
  }
11045
11290
  };
11046
11291
 
11047
- export { adapters_exports as Adapters, backlog_adapter_exports as BacklogAdapter, config_manager_exports as Config, crypto_exports as Crypto, diagram_generator_exports as DiagramGenerator, event_bus_exports as EventBus, execution_adapter_exports as ExecutionAdapter, record_factories_exports as Factories, feedback_adapter_exports as FeedbackAdapter, file_lister_exports as FileLister, finding_detector_exports as FindingDetector, git_exports as Git, identity_adapter_exports as IdentityAdapter, key_provider_exports as KeyProvider, lint_exports as Lint, logger_exports as Logger, project_adapter_exports as ProjectAdapter, project_initializer_exports as ProjectInitializer, record_metrics_exports as RecordMetrics, record_projection_exports as RecordProjection, record_types_exports as Records, agent_runner_exports as Runner, record_schemas_exports as Schemas, session_manager_exports as Session, source_auditor_exports as SourceAuditor, record_store_exports as Store, sync_state_exports as SyncState, record_validations_exports as Validation, workflow_adapter_exports as WorkflowAdapter };
11292
+ export { adapters_exports as Adapters, backlog_adapter_exports as BacklogAdapter, config_manager_exports as Config, crypto_exports as Crypto, diagram_generator_exports as DiagramGenerator, event_bus_exports as EventBus, execution_adapter_exports as ExecutionAdapter, record_factories_exports as Factories, feedback_adapter_exports as FeedbackAdapter, file_lister_exports as FileLister, finding_detector_exports as FindingDetector, git_exports as Git, hook_handler_exports as HookHandler, identity_adapter_exports as IdentityAdapter, key_provider_exports as KeyProvider, lint_exports as Lint, logger_exports as Logger, project_adapter_exports as ProjectAdapter, project_initializer_exports as ProjectInitializer, record_metrics_exports as RecordMetrics, record_projection_exports as RecordProjection, record_types_exports as Records, agent_runner_exports as Runner, record_schemas_exports as Schemas, session_manager_exports as Session, source_auditor_exports as SourceAuditor, record_store_exports as Store, sync_state_exports as SyncState, record_validations_exports as Validation, workflow_adapter_exports as WorkflowAdapter };
11048
11293
  //# sourceMappingURL=index.js.map
11049
11294
  //# sourceMappingURL=index.js.map