@gitgov/core 1.4.0 → 1.5.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.
@@ -2585,10 +2585,6 @@ declare class ProjectAdapter implements IProjectAdapter {
2585
2585
  * [EARS-3] Processes blueprint template JSON with schema validation creating cycles and tasks
2586
2586
  */
2587
2587
  processBlueprintTemplate(templatePath: string, projectContext: ProjectContext): Promise<TemplateProcessingResult>;
2588
- /**
2589
- * Sets up Kiro IDE integration by always copying GitGovernance hooks
2590
- */
2591
- private setupKiroIntegration;
2592
2588
  /**
2593
2589
  * [EARS-4] Cleans up partial setup artifacts if initialization fails
2594
2590
  */
@@ -2606,6 +2602,7 @@ declare class ProjectAdapter implements IProjectAdapter {
2606
2602
  */
2607
2603
  generateProjectReport(): Promise<ProjectReport>;
2608
2604
  private createDirectoryStructure;
2605
+ private copyAgentPrompt;
2609
2606
  private generateProjectId;
2610
2607
  private persistConfiguration;
2611
2608
  private initializeSession;
@@ -2697,6 +2694,13 @@ declare function calculatePayloadChecksum(payload: GitGovRecordPayload): string;
2697
2694
  /**
2698
2695
  * Generates a new Ed25519 key pair.
2699
2696
  * @returns A promise that resolves to an object with publicKey and privateKey in base64 format.
2697
+ *
2698
+ * The publicKey is the raw Ed25519 key (32 bytes -> 44 chars in base64).
2699
+ * The privateKey is stored in PKCS8 PEM format for compatibility.
2700
+ *
2701
+ * Note: Node.js crypto does not support 'raw' format directly for Ed25519,
2702
+ * so we extract the raw 32-byte key from the SPKI DER encoding (RFC 8410).
2703
+ * SPKI DER structure: [algorithm identifier (12 bytes)] + [raw public key (32 bytes)]
2700
2704
  */
2701
2705
  declare function generateKeys(): Promise<{
2702
2706
  publicKey: string;
@@ -2708,6 +2712,8 @@ declare function generateKeys(): Promise<{
2708
2712
  declare function signPayload(payload: GitGovRecordPayload, privateKey: string, keyId: string, role: string, notes: string): Signature;
2709
2713
  /**
2710
2714
  * Verifies all signatures on a record.
2715
+ *
2716
+ * Reconstructs SPKI DER format from raw Ed25519 key for verification.
2711
2717
  */
2712
2718
  declare function verifySignatures(record: {
2713
2719
  header: {
package/dist/src/index.js CHANGED
@@ -5,7 +5,7 @@ import { promises, existsSync, constants } from 'fs';
5
5
  import * as yaml from 'js-yaml';
6
6
  import { generateKeyPair, createHash, sign, verify } from 'crypto';
7
7
  import { promisify } from 'util';
8
- import * as pathUtils from 'path';
8
+ import * as path from 'path';
9
9
  import { EventEmitter } from 'events';
10
10
 
11
11
  var __defProp = Object.defineProperty;
@@ -2672,11 +2672,13 @@ var logger2 = createLogger("[CryptoModule] ");
2672
2672
  var generateKeyPairAsync = promisify(generateKeyPair);
2673
2673
  async function generateKeys() {
2674
2674
  const { publicKey, privateKey } = await generateKeyPairAsync("ed25519", {
2675
- publicKeyEncoding: { type: "spki", format: "pem" },
2675
+ publicKeyEncoding: { type: "spki", format: "der" },
2676
2676
  privateKeyEncoding: { type: "pkcs8", format: "pem" }
2677
2677
  });
2678
+ const rawPublicKey = publicKey.subarray(-32);
2678
2679
  return {
2679
- publicKey: Buffer.from(publicKey).toString("base64"),
2680
+ publicKey: rawPublicKey.toString("base64"),
2681
+ // 32 bytes -> 44 chars
2680
2682
  privateKey: Buffer.from(privateKey).toString("base64")
2681
2683
  };
2682
2684
  }
@@ -2700,20 +2702,36 @@ function signPayload(payload, privateKey, keyId, role, notes) {
2700
2702
  }
2701
2703
  async function verifySignatures(record, getActorPublicKey) {
2702
2704
  for (const signature of record.header.signatures) {
2703
- const publicKey = await getActorPublicKey(signature.keyId);
2704
- if (!publicKey) {
2705
+ const publicKeyBase64 = await getActorPublicKey(signature.keyId);
2706
+ if (!publicKeyBase64) {
2705
2707
  logger2.warn(`Public key not found for actor: ${signature.keyId}`);
2706
2708
  return false;
2707
2709
  }
2708
2710
  const digest = `${record.header.payloadChecksum}:${signature.keyId}:${signature.role}:${signature.notes}:${signature.timestamp}`;
2709
2711
  const digestHash = createHash("sha256").update(digest).digest();
2712
+ const algorithmIdentifier = Buffer.from([
2713
+ 48,
2714
+ 42,
2715
+ 48,
2716
+ 5,
2717
+ 6,
2718
+ 3,
2719
+ 43,
2720
+ 101,
2721
+ 112,
2722
+ 3,
2723
+ 33,
2724
+ 0
2725
+ ]);
2726
+ const rawPublicKey = Buffer.from(publicKeyBase64, "base64");
2727
+ const spkiPublicKey = Buffer.concat([algorithmIdentifier, rawPublicKey]);
2710
2728
  const isValid = verify(
2711
2729
  null,
2712
2730
  digestHash,
2713
2731
  {
2714
- key: Buffer.from(publicKey, "base64"),
2732
+ key: spkiPublicKey,
2715
2733
  type: "spki",
2716
- format: "pem"
2734
+ format: "der"
2717
2735
  },
2718
2736
  Buffer.from(signature.signature, "base64")
2719
2737
  );
@@ -2975,8 +2993,8 @@ var ConfigManager = class _ConfigManager {
2975
2993
  configPath;
2976
2994
  sessionPath;
2977
2995
  constructor(projectRootPath = _ConfigManager.findProjectRoot() || process.cwd()) {
2978
- this.configPath = pathUtils.join(projectRootPath, ".gitgov", "config.json");
2979
- this.sessionPath = pathUtils.join(projectRootPath, ".gitgov", ".session.json");
2996
+ this.configPath = path.join(projectRootPath, ".gitgov", "config.json");
2997
+ this.sessionPath = path.join(projectRootPath, ".gitgov", ".session.json");
2980
2998
  }
2981
2999
  /**
2982
3000
  * Load GitGovernance configuration
@@ -3066,14 +3084,14 @@ var ConfigManager = class _ConfigManager {
3066
3084
  }
3067
3085
  lastSearchPath = startPath;
3068
3086
  let currentPath = startPath;
3069
- while (currentPath !== pathUtils.parse(currentPath).root) {
3070
- if (existsSync(pathUtils.join(currentPath, ".git"))) {
3087
+ while (currentPath !== path.parse(currentPath).root) {
3088
+ if (existsSync(path.join(currentPath, ".git"))) {
3071
3089
  projectRoot = currentPath;
3072
3090
  return projectRoot;
3073
3091
  }
3074
- currentPath = pathUtils.dirname(currentPath);
3092
+ currentPath = path.dirname(currentPath);
3075
3093
  }
3076
- if (existsSync(pathUtils.join(currentPath, ".git"))) {
3094
+ if (existsSync(path.join(currentPath, ".git"))) {
3077
3095
  projectRoot = currentPath;
3078
3096
  return projectRoot;
3079
3097
  }
@@ -3087,23 +3105,23 @@ var ConfigManager = class _ConfigManager {
3087
3105
  */
3088
3106
  static findGitgovRoot(startPath = process.cwd()) {
3089
3107
  let currentPath = startPath;
3090
- while (currentPath !== pathUtils.parse(currentPath).root) {
3091
- if (existsSync(pathUtils.join(currentPath, ".gitgov"))) {
3108
+ while (currentPath !== path.parse(currentPath).root) {
3109
+ if (existsSync(path.join(currentPath, ".gitgov"))) {
3092
3110
  return currentPath;
3093
3111
  }
3094
- currentPath = pathUtils.dirname(currentPath);
3112
+ currentPath = path.dirname(currentPath);
3095
3113
  }
3096
- if (existsSync(pathUtils.join(currentPath, ".gitgov"))) {
3114
+ if (existsSync(path.join(currentPath, ".gitgov"))) {
3097
3115
  return currentPath;
3098
3116
  }
3099
3117
  currentPath = startPath;
3100
- while (currentPath !== pathUtils.parse(currentPath).root) {
3101
- if (existsSync(pathUtils.join(currentPath, ".git"))) {
3118
+ while (currentPath !== path.parse(currentPath).root) {
3119
+ if (existsSync(path.join(currentPath, ".git"))) {
3102
3120
  return currentPath;
3103
3121
  }
3104
- currentPath = pathUtils.dirname(currentPath);
3122
+ currentPath = path.dirname(currentPath);
3105
3123
  }
3106
- if (existsSync(pathUtils.join(currentPath, ".git"))) {
3124
+ if (existsSync(path.join(currentPath, ".git"))) {
3107
3125
  return currentPath;
3108
3126
  }
3109
3127
  return null;
@@ -3116,7 +3134,7 @@ var ConfigManager = class _ConfigManager {
3116
3134
  if (!root) {
3117
3135
  throw new Error("Could not find project root. Make sure you are inside a GitGovernance repository.");
3118
3136
  }
3119
- return pathUtils.join(root, ".gitgov");
3137
+ return path.join(root, ".gitgov");
3120
3138
  }
3121
3139
  /**
3122
3140
  * Checks if current directory is a GitGovernance project
@@ -3145,12 +3163,12 @@ var RecordStore = class {
3145
3163
  throw new Error("Could not find project root. RecordStore requires a valid project root.");
3146
3164
  }
3147
3165
  this.recordType = recordType;
3148
- this.recordsDir = pathUtils.join(foundRoot, ".gitgov", this.recordType);
3166
+ this.recordsDir = path.join(foundRoot, ".gitgov", this.recordType);
3149
3167
  this.fs = fsDeps;
3150
3168
  }
3151
3169
  getRecordPath(recordId) {
3152
3170
  const safeId = recordId.replace(/:/g, "_");
3153
- return pathUtils.join(this.recordsDir, `${safeId}.json`);
3171
+ return path.join(this.recordsDir, `${safeId}.json`);
3154
3172
  }
3155
3173
  async ensureDirExists() {
3156
3174
  await this.fs.mkdir(this.recordsDir, { recursive: true });
@@ -6251,7 +6269,7 @@ var FileIndexerAdapter = class {
6251
6269
  * Writes cache data to file (Phase 1: JSON)
6252
6270
  */
6253
6271
  async writeCacheFile(indexData) {
6254
- const cacheDir = pathUtils.dirname(this.cachePath);
6272
+ const cacheDir = path.dirname(this.cachePath);
6255
6273
  await promises.mkdir(cacheDir, { recursive: true });
6256
6274
  const jsonContent = JSON.stringify(indexData, null, 2);
6257
6275
  await promises.writeFile(this.cachePath, jsonContent, "utf-8");
@@ -6405,10 +6423,10 @@ var FileIndexerAdapter = class {
6405
6423
  let recentActivity = "Task created";
6406
6424
  try {
6407
6425
  let projectRoot2 = process.cwd();
6408
- while (!fs.existsSync(pathUtils.join(projectRoot2, ".gitgov")) && projectRoot2 !== "/") {
6409
- projectRoot2 = pathUtils.dirname(projectRoot2);
6426
+ while (!fs.existsSync(path.join(projectRoot2, ".gitgov")) && projectRoot2 !== "/") {
6427
+ projectRoot2 = path.dirname(projectRoot2);
6410
6428
  }
6411
- const taskFilePath = pathUtils.join(projectRoot2, ".gitgov", "tasks", `${task.id}.json`);
6429
+ const taskFilePath = path.join(projectRoot2, ".gitgov", "tasks", `${task.id}.json`);
6412
6430
  const stats = await promises.stat(taskFilePath);
6413
6431
  const fileModTime = stats.mtime.getTime();
6414
6432
  const creationTime = this.getTimestampFromId(task.id) * 1e3;
@@ -6532,8 +6550,9 @@ var ProjectAdapter = class {
6532
6550
  throw new Error(`Environment validation failed: ${envValidation.warnings.join(", ")}`);
6533
6551
  }
6534
6552
  const projectRoot2 = process.env["GITGOV_ORIGINAL_DIR"] || process.cwd();
6535
- const gitgovPath = pathUtils.join(projectRoot2, ".gitgov");
6553
+ const gitgovPath = path.join(projectRoot2, ".gitgov");
6536
6554
  await this.createDirectoryStructure(gitgovPath);
6555
+ await this.copyAgentPrompt(gitgovPath);
6537
6556
  const actor = await this.identityAdapter.createActor(
6538
6557
  {
6539
6558
  type: "human",
@@ -6586,7 +6605,6 @@ var ProjectAdapter = class {
6586
6605
  await this.persistConfiguration(config, gitgovPath);
6587
6606
  await this.initializeSession(actor.id, gitgovPath);
6588
6607
  await this.setupGitIntegration(projectRoot2);
6589
- await this.setupKiroIntegration(projectRoot2);
6590
6608
  const initializationTime = Date.now() - startTime;
6591
6609
  return {
6592
6610
  success: true,
@@ -6596,7 +6614,7 @@ var ProjectAdapter = class {
6596
6614
  actor: {
6597
6615
  id: actor.id,
6598
6616
  displayName: actor.displayName,
6599
- publicKeyPath: pathUtils.join(gitgovPath, "actors", `${actor.id}.json`)
6617
+ publicKeyPath: path.join(gitgovPath, "actors", `${actor.id}.json`)
6600
6618
  },
6601
6619
  template: templateResult ? {
6602
6620
  processed: true,
@@ -6623,7 +6641,7 @@ var ProjectAdapter = class {
6623
6641
  const warnings = [];
6624
6642
  const suggestions = [];
6625
6643
  try {
6626
- const gitPath = pathUtils.join(targetPath, ".git");
6644
+ const gitPath = path.join(targetPath, ".git");
6627
6645
  const isGitRepo = existsSync(gitPath);
6628
6646
  if (!isGitRepo) {
6629
6647
  warnings.push(`Not a Git repository in directory: ${targetPath}`);
@@ -6631,7 +6649,7 @@ var ProjectAdapter = class {
6631
6649
  }
6632
6650
  let hasWritePermissions = false;
6633
6651
  try {
6634
- const testFile = pathUtils.join(targetPath, ".gitgov-test");
6652
+ const testFile = path.join(targetPath, ".gitgov-test");
6635
6653
  await promises.writeFile(testFile, "test");
6636
6654
  await promises.unlink(testFile);
6637
6655
  hasWritePermissions = true;
@@ -6639,7 +6657,7 @@ var ProjectAdapter = class {
6639
6657
  warnings.push("No write permissions in target directory");
6640
6658
  suggestions.push("Ensure you have write permissions in the target directory");
6641
6659
  }
6642
- const gitgovPath = pathUtils.join(targetPath, ".gitgov");
6660
+ const gitgovPath = path.join(targetPath, ".gitgov");
6643
6661
  let isAlreadyInitialized = false;
6644
6662
  try {
6645
6663
  await promises.access(gitgovPath);
@@ -6728,59 +6746,13 @@ var ProjectAdapter = class {
6728
6746
  ]);
6729
6747
  }
6730
6748
  }
6731
- /**
6732
- * Sets up Kiro IDE integration by always copying GitGovernance hooks
6733
- */
6734
- async setupKiroIntegration(projectRoot2) {
6735
- const kiroDir = pathUtils.join(projectRoot2, ".kiro");
6736
- const kiroHooksDir = pathUtils.join(kiroDir, "hooks");
6737
- try {
6738
- await promises.mkdir(kiroHooksDir, { recursive: true });
6739
- } catch {
6740
- }
6741
- const sourceHooksDir = pathUtils.join(ConfigManager.findProjectRoot() || process.cwd(), ".kiro/hooks");
6742
- try {
6743
- await promises.access(sourceHooksDir);
6744
- const essentialHooks = [
6745
- "gitgov-auto-indexer.kiro.hook",
6746
- "git-diagnostics-commit.kiro.hook",
6747
- "gitgov-file-analyzer.kiro.hook",
6748
- "code-quality-analyzer.kiro.hook",
6749
- "gitgov-quick-status.kiro.hook",
6750
- "gitgov-task-creator.kiro.hook",
6751
- "gitgov-work-session.kiro.hook"
6752
- ];
6753
- let copiedHooks = 0;
6754
- for (const hookFile of essentialHooks) {
6755
- try {
6756
- const sourcePath = pathUtils.join(sourceHooksDir, hookFile);
6757
- const targetPath = pathUtils.join(kiroHooksDir, hookFile);
6758
- await promises.copyFile(sourcePath, targetPath);
6759
- copiedHooks++;
6760
- } catch {
6761
- }
6762
- }
6763
- try {
6764
- const sourceGitgovPath = pathUtils.join(ConfigManager.findProjectRoot() || process.cwd(), ".gitgov/gitgov");
6765
- const targetGitgovPath = pathUtils.join(projectRoot2, ".gitgov/gitgov");
6766
- await promises.copyFile(sourceGitgovPath, targetGitgovPath);
6767
- await promises.chmod(targetGitgovPath, 493);
6768
- console.log(`\u{1F4CB} GitGovernance executable copied to .gitgov/gitgov`);
6769
- } catch {
6770
- }
6771
- if (copiedHooks > 0) {
6772
- console.log(`\u{1F527} Kiro IDE Integration: ${copiedHooks} GitGovernance hooks installed`);
6773
- }
6774
- } catch {
6775
- }
6776
- }
6777
6749
  /**
6778
6750
  * [EARS-4] Cleans up partial setup artifacts if initialization fails
6779
6751
  */
6780
6752
  async rollbackPartialSetup(setupId) {
6781
6753
  try {
6782
6754
  const projectRoot2 = process.env["GITGOV_ORIGINAL_DIR"] || process.cwd();
6783
- const gitgovPath = pathUtils.join(projectRoot2, ".gitgov");
6755
+ const gitgovPath = path.join(projectRoot2, ".gitgov");
6784
6756
  try {
6785
6757
  await promises.access(gitgovPath);
6786
6758
  await promises.rm(gitgovPath, { recursive: true, force: true });
@@ -6844,18 +6816,28 @@ var ProjectAdapter = class {
6844
6816
  ];
6845
6817
  await promises.mkdir(gitgovPath, { recursive: true });
6846
6818
  for (const dir of directories) {
6847
- await promises.mkdir(pathUtils.join(gitgovPath, dir), { recursive: true });
6819
+ await promises.mkdir(path.join(gitgovPath, dir), { recursive: true });
6820
+ }
6821
+ }
6822
+ async copyAgentPrompt(gitgovPath) {
6823
+ try {
6824
+ const sourcePrompt = path.join(ConfigManager.findProjectRoot() || process.cwd(), "docs/gitgov_agent_prompt.md");
6825
+ const targetPrompt = path.join(gitgovPath, "gitgov");
6826
+ await promises.copyFile(sourcePrompt, targetPrompt);
6827
+ console.log(`\u{1F4CB} @gitgov agent prompt copied to .gitgov/gitgov`);
6828
+ } catch {
6829
+ console.warn("Warning: Could not copy @gitgov agent prompt. Project will work but AI assistant may not have local instructions.");
6848
6830
  }
6849
6831
  }
6850
6832
  generateProjectId(name) {
6851
6833
  return name.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-");
6852
6834
  }
6853
6835
  async persistConfiguration(config, gitgovPath) {
6854
- const configPath = pathUtils.join(gitgovPath, "config.json");
6836
+ const configPath = path.join(gitgovPath, "config.json");
6855
6837
  await promises.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
6856
6838
  }
6857
6839
  async initializeSession(actorId, gitgovPath) {
6858
- const sessionPath = pathUtils.join(gitgovPath, ".session.json");
6840
+ const sessionPath = path.join(gitgovPath, ".session.json");
6859
6841
  const session = {
6860
6842
  lastSession: {
6861
6843
  actorId,
@@ -6870,7 +6852,7 @@ var ProjectAdapter = class {
6870
6852
  await promises.writeFile(sessionPath, JSON.stringify(session, null, 2), "utf-8");
6871
6853
  }
6872
6854
  async setupGitIntegration(projectRoot2) {
6873
- const gitignorePath = pathUtils.join(projectRoot2, ".gitignore");
6855
+ const gitignorePath = path.join(projectRoot2, ".gitignore");
6874
6856
  const gitignoreContent = `
6875
6857
  # GitGovernance
6876
6858
  .gitgov/.session.json
@@ -8983,14 +8965,14 @@ var DiagramGenerator = class {
8983
8965
  * Loads all cycle records from the filesystem
8984
8966
  */
8985
8967
  async loadCycleRecords(gitgovPath) {
8986
- const cyclesDir = pathUtils.join(gitgovPath, "cycles");
8968
+ const cyclesDir = path.join(gitgovPath, "cycles");
8987
8969
  try {
8988
8970
  const files = await promises.readdir(cyclesDir);
8989
8971
  const jsonFiles = files.filter((file) => file.endsWith(".json"));
8990
8972
  const cycles = [];
8991
8973
  for (const file of jsonFiles) {
8992
8974
  try {
8993
- const filePath = pathUtils.join(cyclesDir, file);
8975
+ const filePath = path.join(cyclesDir, file);
8994
8976
  const content = await promises.readFile(filePath, "utf-8");
8995
8977
  const record = JSON.parse(content);
8996
8978
  if (record.payload && record.payload.id) {
@@ -9019,14 +9001,14 @@ var DiagramGenerator = class {
9019
9001
  * Loads all task records from the filesystem
9020
9002
  */
9021
9003
  async loadTaskRecords(gitgovPath) {
9022
- const tasksDir = pathUtils.join(gitgovPath, "tasks");
9004
+ const tasksDir = path.join(gitgovPath, "tasks");
9023
9005
  try {
9024
9006
  const files = await promises.readdir(tasksDir);
9025
9007
  const jsonFiles = files.filter((file) => file.endsWith(".json"));
9026
9008
  const tasks = [];
9027
9009
  for (const file of jsonFiles) {
9028
9010
  try {
9029
- const filePath = pathUtils.join(tasksDir, file);
9011
+ const filePath = path.join(tasksDir, file);
9030
9012
  const content = await promises.readFile(filePath, "utf-8");
9031
9013
  const record = JSON.parse(content);
9032
9014
  if (record.payload && record.payload.id) {