@shardworks/nexus-core 0.1.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.
Files changed (53) hide show
  1. package/dist/base-tools.d.ts +27 -0
  2. package/dist/base-tools.d.ts.map +1 -0
  3. package/dist/base-tools.js +30 -0
  4. package/dist/base-tools.js.map +1 -0
  5. package/dist/bootstrap.d.ts +14 -0
  6. package/dist/bootstrap.d.ts.map +1 -0
  7. package/dist/bootstrap.js +46 -0
  8. package/dist/bootstrap.js.map +1 -0
  9. package/dist/dispatch.d.ts +26 -0
  10. package/dist/dispatch.d.ts.map +1 -0
  11. package/dist/dispatch.js +55 -0
  12. package/dist/dispatch.js.map +1 -0
  13. package/dist/guild-config.d.ts +51 -0
  14. package/dist/guild-config.d.ts.map +1 -0
  15. package/dist/guild-config.js +33 -0
  16. package/dist/guild-config.js.map +1 -0
  17. package/dist/implement.d.ts +69 -0
  18. package/dist/implement.d.ts.map +1 -0
  19. package/dist/implement.js +47 -0
  20. package/dist/implement.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +15 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/init-guild.d.ts +20 -0
  26. package/dist/init-guild.d.ts.map +1 -0
  27. package/dist/init-guild.js +77 -0
  28. package/dist/init-guild.js.map +1 -0
  29. package/dist/install-tool.d.ts +34 -0
  30. package/dist/install-tool.d.ts.map +1 -0
  31. package/dist/install-tool.js +149 -0
  32. package/dist/install-tool.js.map +1 -0
  33. package/dist/instantiate.d.ts +28 -0
  34. package/dist/instantiate.d.ts.map +1 -0
  35. package/dist/instantiate.js +113 -0
  36. package/dist/instantiate.js.map +1 -0
  37. package/dist/ledger.d.ts +8 -0
  38. package/dist/ledger.d.ts.map +1 -0
  39. package/dist/ledger.js +75 -0
  40. package/dist/ledger.js.map +1 -0
  41. package/dist/nexus-home.d.ts +14 -0
  42. package/dist/nexus-home.d.ts.map +1 -0
  43. package/dist/nexus-home.js +29 -0
  44. package/dist/nexus-home.js.map +1 -0
  45. package/dist/publish.d.ts +20 -0
  46. package/dist/publish.d.ts.map +1 -0
  47. package/dist/publish.js +41 -0
  48. package/dist/publish.js.map +1 -0
  49. package/dist/remove-tool.d.ts +20 -0
  50. package/dist/remove-tool.d.ts.map +1 -0
  51. package/dist/remove-tool.js +64 -0
  52. package/dist/remove-tool.js.map +1 -0
  53. package/package.json +33 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-tool.d.ts","sourceRoot":"","sources":["../src/install-tool.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAkDD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAmFnE"}
@@ -0,0 +1,149 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { guildhallWorktreePath } from "./nexus-home.js";
5
+ import { readGuildConfig, writeGuildConfig } from "./guild-config.js";
6
+ /** Descriptor file names in priority order for detection. */
7
+ const DESCRIPTOR_FILES = [
8
+ 'nexus-implement.json',
9
+ 'nexus-engine.json',
10
+ 'nexus-curriculum.json',
11
+ 'nexus-temperament.json',
12
+ ];
13
+ /** Map descriptor file → artifact category in guild.json */
14
+ const CATEGORY_MAP = {
15
+ 'nexus-implement.json': 'implements',
16
+ 'nexus-engine.json': 'engines',
17
+ 'nexus-curriculum.json': 'curricula',
18
+ 'nexus-temperament.json': 'temperaments',
19
+ };
20
+ /** Map category → on-disk parent directory (relative to guildhall worktree). */
21
+ const DIR_MAP = {
22
+ implements: 'implements',
23
+ engines: 'engines',
24
+ curricula: 'training/curricula',
25
+ temperaments: 'training/temperaments',
26
+ };
27
+ function git(args, cwd) {
28
+ execFileSync('git', args, { cwd, stdio: 'pipe' });
29
+ }
30
+ /** Detect the descriptor file in a directory. Returns the filename or throws. */
31
+ function findDescriptor(dir) {
32
+ for (const f of DESCRIPTOR_FILES) {
33
+ if (fs.existsSync(path.join(dir, f)))
34
+ return f;
35
+ }
36
+ throw new Error(`No descriptor found in ${dir}. Expected one of: ${DESCRIPTOR_FILES.join(', ')}`);
37
+ }
38
+ /** Read a JSON file, returning an empty object on missing fields. */
39
+ function readJson(filePath) {
40
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
41
+ }
42
+ /** Directories to skip when copying tool sources. */
43
+ const SKIP_DIRS = new Set(['node_modules', '.git']);
44
+ /** Recursively copy a directory, skipping node_modules and .git. */
45
+ function copyDir(src, dest) {
46
+ fs.mkdirSync(dest, { recursive: true });
47
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
48
+ const srcPath = path.join(src, entry.name);
49
+ const destPath = path.join(dest, entry.name);
50
+ if (entry.isDirectory()) {
51
+ if (!SKIP_DIRS.has(entry.name)) {
52
+ copyDir(srcPath, destPath);
53
+ }
54
+ }
55
+ else if (entry.isSymbolicLink()) {
56
+ // Resolve symlink and copy the target
57
+ const realPath = fs.realpathSync(srcPath);
58
+ if (fs.statSync(realPath).isDirectory()) {
59
+ if (!SKIP_DIRS.has(entry.name)) {
60
+ copyDir(realPath, destPath);
61
+ }
62
+ }
63
+ else {
64
+ fs.copyFileSync(realPath, destPath);
65
+ }
66
+ }
67
+ else {
68
+ fs.copyFileSync(srcPath, destPath);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Install a tool (implement, engine, curriculum, or temperament) into the guild.
74
+ *
75
+ * Currently supports local directory sources only. Detects the descriptor type,
76
+ * copies the directory to the correct slot, and registers in guild.json.
77
+ */
78
+ export function installTool(opts) {
79
+ const { home, source, roles, framework = false, commit = true } = opts;
80
+ const worktree = guildhallWorktreePath(home);
81
+ // Resolve source to absolute path
82
+ const sourceDir = path.resolve(source);
83
+ if (!fs.existsSync(sourceDir) || !fs.statSync(sourceDir).isDirectory()) {
84
+ throw new Error(`Source is not a directory: ${sourceDir}`);
85
+ }
86
+ // Detect descriptor
87
+ const descriptorFile = findDescriptor(sourceDir);
88
+ const category = CATEGORY_MAP[descriptorFile];
89
+ const descriptor = readJson(path.join(sourceDir, descriptorFile));
90
+ // Read optional package.json for fallbacks
91
+ const pkgJsonPath = path.join(sourceDir, 'package.json');
92
+ const pkg = fs.existsSync(pkgJsonPath) ? readJson(pkgJsonPath) : {};
93
+ // Resolve name: --name flag > directory basename
94
+ const name = opts.name || path.basename(sourceDir);
95
+ if (!name || name === '.' || name === '..') {
96
+ throw new Error('Could not determine tool name. Use --name to specify one.');
97
+ }
98
+ // Resolve slot: --slot flag > descriptor version > package.json version > error
99
+ const version = descriptor['version']
100
+ || pkg['version'];
101
+ const slot = opts.slot || version;
102
+ if (!slot) {
103
+ throw new Error('No version found in descriptor or package.json. Use --slot to specify a version slot.');
104
+ }
105
+ // Determine target directory.
106
+ // Framework tools go under nexus/{implements,engines}/.
107
+ // Guild tools go under {implements,engines}/ (or training/ for curricula/temperaments).
108
+ const parentDir = DIR_MAP[category];
109
+ const prefix = framework && (category === 'implements' || category === 'engines')
110
+ ? path.join('nexus', parentDir)
111
+ : parentDir;
112
+ const targetDir = path.join(worktree, prefix, name, slot);
113
+ // Copy source to target
114
+ if (fs.existsSync(targetDir)) {
115
+ fs.rmSync(targetDir, { recursive: true });
116
+ }
117
+ copyDir(sourceDir, targetDir);
118
+ // Register in guild.json
119
+ const config = readGuildConfig(home);
120
+ const now = new Date().toISOString();
121
+ if (category === 'implements' || category === 'engines') {
122
+ const entry = {
123
+ source: framework ? 'nexus' : 'guild',
124
+ slot,
125
+ upstream: null,
126
+ installedAt: now,
127
+ };
128
+ if (category === 'implements' && roles && roles.length > 0) {
129
+ entry.roles = roles;
130
+ }
131
+ config[category][name] = entry;
132
+ }
133
+ else {
134
+ const entry = {
135
+ slot,
136
+ upstream: null,
137
+ installedAt: now,
138
+ };
139
+ config[category][name] = entry;
140
+ }
141
+ writeGuildConfig(home, config);
142
+ // Commit (unless suppressed — e.g. during bootstrap)
143
+ if (commit) {
144
+ git(['add', '-A'], worktree);
145
+ git(['commit', '-m', `Install ${category.slice(0, -1)} ${name}@${slot}`], worktree);
146
+ }
147
+ return { category, name, slot, installedTo: targetDir };
148
+ }
149
+ //# sourceMappingURL=install-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-tool.js","sourceRoot":"","sources":["../src/install-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface InstantiateOptions {
2
+ /** Absolute path to NEXUS_HOME. */
3
+ home: string;
4
+ /** Name for the new anima. Must be unique within the guild. */
5
+ name: string;
6
+ /** Roles the anima will hold (determines implement access via role gating). */
7
+ roles: string[];
8
+ /** Curriculum to assign (by name, must be registered in guild.json). */
9
+ curriculum?: string;
10
+ /** Temperament to assign (by name, must be registered in guild.json). */
11
+ temperament?: string;
12
+ }
13
+ export interface InstantiateResult {
14
+ animaId: number;
15
+ name: string;
16
+ roles: string[];
17
+ curriculum: string | null;
18
+ temperament: string | null;
19
+ }
20
+ /**
21
+ * Instantiate a new anima in the guild.
22
+ *
23
+ * Creates the anima record, assigns roles in the roster, and snapshots the
24
+ * composition (curriculum + temperament content at current versions). All
25
+ * operations happen in a single transaction.
26
+ */
27
+ export declare function instantiate(opts: InstantiateOptions): InstantiateResult;
28
+ //# sourceMappingURL=instantiate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instantiate.d.ts","sourceRoot":"","sources":["../src/instantiate.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,kBAAkB;IACjC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA6CD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAyGvE"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * instantiate — core logic for creating new animas.
3
+ *
4
+ * Creates an anima record in the Ledger with its full composition: roles,
5
+ * curriculum, and temperament. Reads and snapshots the training content at
6
+ * instantiation time so the anima's composition is frozen to specific versions.
7
+ */
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import Database from 'better-sqlite3';
11
+ import { ledgerPath, guildhallWorktreePath } from "./nexus-home.js";
12
+ import { readGuildConfig } from "./guild-config.js";
13
+ /**
14
+ * Read training content from disk given its guild.json entry.
15
+ *
16
+ * @param worktree - Path to the guildhall worktree.
17
+ * @param category - 'curricula' or 'temperaments'.
18
+ * @param name - The training content name.
19
+ * @param slot - The version slot.
20
+ * @returns Object with version and the actual content text.
21
+ */
22
+ function readTrainingContent(worktree, category, name, slot) {
23
+ const dir = path.join(worktree, 'training', category, name, slot);
24
+ const descriptorFile = category === 'curricula'
25
+ ? 'nexus-curriculum.json'
26
+ : 'nexus-temperament.json';
27
+ const descriptorPath = path.join(dir, descriptorFile);
28
+ if (!fs.existsSync(descriptorPath)) {
29
+ throw new Error(`${category.slice(0, -1)} "${name}" slot "${slot}" not found on disk at ${dir}`);
30
+ }
31
+ const descriptor = JSON.parse(fs.readFileSync(descriptorPath, 'utf-8'));
32
+ const contentFile = descriptor.content;
33
+ const contentPath = path.join(dir, contentFile);
34
+ if (!fs.existsSync(contentPath)) {
35
+ throw new Error(`Content file "${contentFile}" not found for ${category.slice(0, -1)} "${name}" at ${contentPath}`);
36
+ }
37
+ return {
38
+ version: slot,
39
+ content: fs.readFileSync(contentPath, 'utf-8'),
40
+ };
41
+ }
42
+ /**
43
+ * Instantiate a new anima in the guild.
44
+ *
45
+ * Creates the anima record, assigns roles in the roster, and snapshots the
46
+ * composition (curriculum + temperament content at current versions). All
47
+ * operations happen in a single transaction.
48
+ */
49
+ export function instantiate(opts) {
50
+ const { home, name, roles, curriculum, temperament } = opts;
51
+ const worktree = guildhallWorktreePath(home);
52
+ if (roles.length === 0) {
53
+ throw new Error('At least one role is required.');
54
+ }
55
+ // Validate curriculum and temperament exist in guild.json
56
+ const config = readGuildConfig(home);
57
+ if (curriculum && !config.curricula[curriculum]) {
58
+ throw new Error(`Curriculum "${curriculum}" not found in guild.json. Available: ${Object.keys(config.curricula).join(', ') || '(none)'}`);
59
+ }
60
+ if (temperament && !config.temperaments[temperament]) {
61
+ throw new Error(`Temperament "${temperament}" not found in guild.json. Available: ${Object.keys(config.temperaments).join(', ') || '(none)'}`);
62
+ }
63
+ // Read and snapshot training content
64
+ let curriculumSnapshot = null;
65
+ if (curriculum) {
66
+ const entry = config.curricula[curriculum];
67
+ const { version, content } = readTrainingContent(worktree, 'curricula', curriculum, entry.slot);
68
+ curriculumSnapshot = { name: curriculum, version, content };
69
+ }
70
+ let temperamentSnapshot = null;
71
+ if (temperament) {
72
+ const entry = config.temperaments[temperament];
73
+ const { version, content } = readTrainingContent(worktree, 'temperaments', temperament, entry.slot);
74
+ temperamentSnapshot = { name: temperament, version, content };
75
+ }
76
+ const db = new Database(ledgerPath(home));
77
+ db.pragma('foreign_keys = ON');
78
+ try {
79
+ const result = db.transaction(() => {
80
+ // Check name uniqueness
81
+ const existing = db.prepare(`SELECT id FROM animas WHERE name = ?`).get(name);
82
+ if (existing) {
83
+ throw new Error(`Anima "${name}" already exists in the Ledger.`);
84
+ }
85
+ // Create anima
86
+ const insertAnima = db.prepare(`INSERT INTO animas (name, status) VALUES (?, 'active')`);
87
+ const animaResult = insertAnima.run(name);
88
+ const animaId = Number(animaResult.lastInsertRowid);
89
+ // Assign roles in roster
90
+ const insertRole = db.prepare(`INSERT INTO roster (anima_id, role) VALUES (?, ?)`);
91
+ for (const role of roles) {
92
+ insertRole.run(animaId, role);
93
+ }
94
+ // Record composition snapshot
95
+ db.prepare(`INSERT INTO anima_compositions (anima_id, curriculum_name, curriculum_version, curriculum_snapshot, temperament_name, temperament_version, temperament_snapshot)
96
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(animaId, curriculumSnapshot?.name ?? '', curriculumSnapshot?.version ?? '', curriculumSnapshot?.content ?? '', temperamentSnapshot?.name ?? '', temperamentSnapshot?.version ?? '', temperamentSnapshot?.content ?? '');
97
+ // Audit log
98
+ db.prepare(`INSERT INTO audit_log (actor, action, target_type, target_id, detail) VALUES (?, ?, ?, ?, ?)`).run('instantiate', 'anima_instantiated', 'anima', animaId, JSON.stringify({ roles, curriculum: curriculum ?? null, temperament: temperament ?? null }));
99
+ return animaId;
100
+ })();
101
+ return {
102
+ animaId: result,
103
+ name,
104
+ roles,
105
+ curriculum: curriculum ?? null,
106
+ temperament: temperament ?? null,
107
+ };
108
+ }
109
+ finally {
110
+ db.close();
111
+ }
112
+ }
113
+ //# sourceMappingURL=instantiate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instantiate.js","sourceRoot":"","sources":["../src/instantiate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAuBpD;;;;;;;;GAQG;AACH,SAAS,mBAAmB,CAC1B,QAAgB,EAChB,QAAsC,EACtC,IAAY,EACZ,IAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAClE,MAAM,cAAc,GAAG,QAAQ,KAAK,WAAW;QAC7C,CAAC,CAAC,uBAAuB;QACzB,CAAC,CAAC,wBAAwB,CAAC;IAC7B,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAEtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,WAAW,IAAI,0BAA0B,GAAG,EAAE,CAChF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;IACxE,MAAM,WAAW,GAAG,UAAU,CAAC,OAAiB,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAEhD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,iBAAiB,WAAW,mBAAmB,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,QAAQ,WAAW,EAAE,CACnG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAwB;IAClD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC5D,MAAM,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAE7C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,0DAA0D;IAC1D,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAErC,IAAI,UAAU,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,eAAe,UAAU,yCAAyC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CACzH,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,gBAAgB,WAAW,yCAAyC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAC9H,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,IAAI,kBAAkB,GAA8D,IAAI,CAAC;IACzF,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,UAAU,CAAE,CAAC;QAC5C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAChG,kBAAkB,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9D,CAAC;IAED,IAAI,mBAAmB,GAA8D,IAAI,CAAC;IAC1F,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,CAAE,CAAC;QAChD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACpG,mBAAmB,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YACjC,wBAAwB;YACxB,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CACzB,sCAAsC,CACvC,CAAC,GAAG,CAAC,IAAI,CAA+B,CAAC;YAE1C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,iCAAiC,CAAC,CAAC;YACnE,CAAC;YAED,eAAe;YACf,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAC5B,wDAAwD,CACzD,CAAC;YACF,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;YAEpD,yBAAyB;YACzB,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAC3B,mDAAmD,CACpD,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;YAED,8BAA8B;YAC9B,EAAE,CAAC,OAAO,CACR;sCAC8B,CAC/B,CAAC,GAAG,CACH,OAAO,EACP,kBAAkB,EAAE,IAAI,IAAI,EAAE,EAC9B,kBAAkB,EAAE,OAAO,IAAI,EAAE,EACjC,kBAAkB,EAAE,OAAO,IAAI,EAAE,EACjC,mBAAmB,EAAE,IAAI,IAAI,EAAE,EAC/B,mBAAmB,EAAE,OAAO,IAAI,EAAE,EAClC,mBAAmB,EAAE,OAAO,IAAI,EAAE,CACnC,CAAC;YAEF,YAAY;YACZ,EAAE,CAAC,OAAO,CACR,8FAA8F,CAC/F,CAAC,GAAG,CACH,aAAa,EACb,oBAAoB,EACpB,OAAO,EACP,OAAO,EACP,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,IAAI,IAAI,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI,EAAE,CAAC,CAC5F,CAAC;YAEF,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO;YACL,OAAO,EAAE,MAAgB;YACzB,IAAI;YACJ,KAAK;YACL,UAAU,EAAE,UAAU,IAAI,IAAI;YAC9B,WAAW,EAAE,WAAW,IAAI,IAAI;SACjC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** SQL for the initial Ledger schema (001). Creates all base tables with WAL mode and foreign keys. */
2
+ export declare const INITIAL_SCHEMA = "\nPRAGMA journal_mode = WAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE animas (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n status TEXT NOT NULL CHECK(status IN ('aspirant', 'active', 'retired')),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE anima_compositions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n anima_id INTEGER NOT NULL UNIQUE REFERENCES animas(id),\n curriculum_name TEXT NOT NULL,\n curriculum_version TEXT NOT NULL,\n temperament_name TEXT NOT NULL,\n temperament_version TEXT NOT NULL,\n curriculum_snapshot TEXT NOT NULL,\n temperament_snapshot TEXT NOT NULL,\n composed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE roster (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n anima_id INTEGER NOT NULL REFERENCES animas(id),\n role TEXT NOT NULL,\n standing INTEGER NOT NULL DEFAULT 0,\n assigned_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE commissions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n content TEXT NOT NULL,\n status TEXT NOT NULL CHECK(status IN ('posted', 'assigned', 'in_progress', 'completed', 'failed')),\n workshop TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE commission_assignments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n commission_id INTEGER NOT NULL REFERENCES commissions(id),\n anima_id INTEGER NOT NULL REFERENCES animas(id),\n assigned_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(commission_id, anima_id)\n);\n\nCREATE TABLE audit_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n actor TEXT NOT NULL,\n action TEXT NOT NULL,\n target_type TEXT,\n target_id INTEGER,\n detail TEXT,\n timestamp TEXT NOT NULL DEFAULT (datetime('now'))\n);\n";
3
+ /**
4
+ * Create a new Ledger database at the given path and apply the initial schema.
5
+ * @param dbPath - Absolute path where the SQLite file will be created.
6
+ */
7
+ export declare function createLedger(dbPath: string): void;
8
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAEA,uGAAuG;AACvG,eAAO,MAAM,cAAc,ipEA0D1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOjD"}
package/dist/ledger.js ADDED
@@ -0,0 +1,75 @@
1
+ import Database from 'better-sqlite3';
2
+ /** SQL for the initial Ledger schema (001). Creates all base tables with WAL mode and foreign keys. */
3
+ export const INITIAL_SCHEMA = `
4
+ PRAGMA journal_mode = WAL;
5
+ PRAGMA foreign_keys = ON;
6
+
7
+ CREATE TABLE animas (
8
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9
+ name TEXT NOT NULL UNIQUE,
10
+ status TEXT NOT NULL CHECK(status IN ('aspirant', 'active', 'retired')),
11
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
12
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
13
+ );
14
+
15
+ CREATE TABLE anima_compositions (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ anima_id INTEGER NOT NULL UNIQUE REFERENCES animas(id),
18
+ curriculum_name TEXT NOT NULL,
19
+ curriculum_version TEXT NOT NULL,
20
+ temperament_name TEXT NOT NULL,
21
+ temperament_version TEXT NOT NULL,
22
+ curriculum_snapshot TEXT NOT NULL,
23
+ temperament_snapshot TEXT NOT NULL,
24
+ composed_at TEXT NOT NULL DEFAULT (datetime('now'))
25
+ );
26
+
27
+ CREATE TABLE roster (
28
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
29
+ anima_id INTEGER NOT NULL REFERENCES animas(id),
30
+ role TEXT NOT NULL,
31
+ standing INTEGER NOT NULL DEFAULT 0,
32
+ assigned_at TEXT NOT NULL DEFAULT (datetime('now'))
33
+ );
34
+
35
+ CREATE TABLE commissions (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ content TEXT NOT NULL,
38
+ status TEXT NOT NULL CHECK(status IN ('posted', 'assigned', 'in_progress', 'completed', 'failed')),
39
+ workshop TEXT NOT NULL,
40
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
41
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
42
+ );
43
+
44
+ CREATE TABLE commission_assignments (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ commission_id INTEGER NOT NULL REFERENCES commissions(id),
47
+ anima_id INTEGER NOT NULL REFERENCES animas(id),
48
+ assigned_at TEXT NOT NULL DEFAULT (datetime('now')),
49
+ UNIQUE(commission_id, anima_id)
50
+ );
51
+
52
+ CREATE TABLE audit_log (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ actor TEXT NOT NULL,
55
+ action TEXT NOT NULL,
56
+ target_type TEXT,
57
+ target_id INTEGER,
58
+ detail TEXT,
59
+ timestamp TEXT NOT NULL DEFAULT (datetime('now'))
60
+ );
61
+ `;
62
+ /**
63
+ * Create a new Ledger database at the given path and apply the initial schema.
64
+ * @param dbPath - Absolute path where the SQLite file will be created.
65
+ */
66
+ export function createLedger(dbPath) {
67
+ const db = new Database(dbPath);
68
+ try {
69
+ db.exec(INITIAL_SCHEMA);
70
+ }
71
+ finally {
72
+ db.close();
73
+ }
74
+ }
75
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,uGAAuG;AACvG,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0D7B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1B,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve NEXUS_HOME from the environment.
3
+ * @throws If `NEXUS_HOME` is not set.
4
+ */
5
+ export declare function resolveNexusHome(): string;
6
+ /** Path to the guildhall bare git repo. */
7
+ export declare function guildhallBarePath(home: string): string;
8
+ /** Path to the guildhall's standing worktree (`worktrees/guildhall/main`). */
9
+ export declare function guildhallWorktreePath(home: string): string;
10
+ /** Path to the Ledger SQLite database. */
11
+ export declare function ledgerPath(home: string): string;
12
+ /** Path to the top-level worktrees directory. */
13
+ export declare function worktreesPath(home: string): string;
14
+ //# sourceMappingURL=nexus-home.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nexus-home.d.ts","sourceRoot":"","sources":["../src/nexus-home.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,29 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Resolve NEXUS_HOME from the environment.
4
+ * @throws If `NEXUS_HOME` is not set.
5
+ */
6
+ export function resolveNexusHome() {
7
+ const home = process.env['NEXUS_HOME'];
8
+ if (!home) {
9
+ throw new Error('NEXUS_HOME is not set. Run `nexus init <path>` to create a guild, then set NEXUS_HOME to that path.');
10
+ }
11
+ return path.resolve(home);
12
+ }
13
+ /** Path to the guildhall bare git repo. */
14
+ export function guildhallBarePath(home) {
15
+ return path.join(home, 'guildhall');
16
+ }
17
+ /** Path to the guildhall's standing worktree (`worktrees/guildhall/main`). */
18
+ export function guildhallWorktreePath(home) {
19
+ return path.join(home, 'worktrees', 'guildhall', 'main');
20
+ }
21
+ /** Path to the Ledger SQLite database. */
22
+ export function ledgerPath(home) {
23
+ return path.join(home, 'nexus.db');
24
+ }
25
+ /** Path to the top-level worktrees directory. */
26
+ export function worktreesPath(home) {
27
+ return path.join(home, 'worktrees');
28
+ }
29
+ //# sourceMappingURL=nexus-home.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nexus-home.js","sourceRoot":"","sources":["../src/nexus-home.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,qGAAqG,CAAC,CAAC;IACzH,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface PublishOptions {
2
+ /** Absolute path to NEXUS_HOME. */
3
+ home: string;
4
+ /** Commission ID to publish. */
5
+ commissionId: number;
6
+ /** Brief summary of what was accomplished. */
7
+ summary?: string;
8
+ }
9
+ export interface PublishResult {
10
+ commissionId: number;
11
+ previousStatus: string;
12
+ }
13
+ /**
14
+ * Publish a completed commission.
15
+ *
16
+ * Validates the commission exists and is in an assignable state (assigned or
17
+ * in_progress), then marks it as completed. Records the event in the audit log.
18
+ */
19
+ export declare function publish(opts: PublishOptions): PublishResult;
20
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CA8C3D"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * publish — core logic for completing commissions.
3
+ *
4
+ * Marks a commission as completed in the Ledger. The publish implement
5
+ * and CLI both call this function.
6
+ */
7
+ import Database from 'better-sqlite3';
8
+ import { ledgerPath } from "./nexus-home.js";
9
+ /**
10
+ * Publish a completed commission.
11
+ *
12
+ * Validates the commission exists and is in an assignable state (assigned or
13
+ * in_progress), then marks it as completed. Records the event in the audit log.
14
+ */
15
+ export function publish(opts) {
16
+ const { home, commissionId, summary } = opts;
17
+ const db = new Database(ledgerPath(home));
18
+ db.pragma('foreign_keys = ON');
19
+ try {
20
+ // Read current commission state
21
+ const commission = db.prepare(`SELECT id, status FROM commissions WHERE id = ?`).get(commissionId);
22
+ if (!commission) {
23
+ throw new Error(`Commission ${commissionId} not found in the Ledger.`);
24
+ }
25
+ const completableStatuses = ['assigned', 'in_progress'];
26
+ if (!completableStatuses.includes(commission.status)) {
27
+ throw new Error(`Commission ${commissionId} cannot be published — current status is "${commission.status}". ` +
28
+ `Must be one of: ${completableStatuses.join(', ')}.`);
29
+ }
30
+ const previousStatus = commission.status;
31
+ // Mark as completed
32
+ db.prepare(`UPDATE commissions SET status = 'completed', updated_at = datetime('now') WHERE id = ?`).run(commissionId);
33
+ // Audit log
34
+ db.prepare(`INSERT INTO audit_log (actor, action, target_type, target_id, detail) VALUES (?, ?, ?, ?, ?)`).run('publish', 'commission_published', 'commission', commissionId, JSON.stringify({ previousStatus, summary: summary ?? null }));
35
+ return { commissionId, previousStatus };
36
+ }
37
+ finally {
38
+ db.close();
39
+ }
40
+ }
41
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgB7C;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,IAAoB;IAC1C,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAE7C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,IAAI,CAAC;QACH,gCAAgC;QAChC,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAC3B,iDAAiD,CAClD,CAAC,GAAG,CAAC,YAAY,CAA+C,CAAC;QAElE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,cAAc,YAAY,2BAA2B,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,mBAAmB,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,cAAc,YAAY,6CAA6C,UAAU,CAAC,MAAM,KAAK;gBAC7F,mBAAmB,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACrD,CAAC;QACJ,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;QAEzC,oBAAoB;QACpB,EAAE,CAAC,OAAO,CACR,wFAAwF,CACzF,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAEpB,YAAY;QACZ,EAAE,CAAC,OAAO,CACR,8FAA8F,CAC/F,CAAC,GAAG,CACH,SAAS,EACT,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACZ,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,CAAC,CAC7D,CAAC;QAEF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface RemoveToolOptions {
2
+ home: string;
3
+ name: string;
4
+ /** Restrict to a specific category. If omitted, searches all registries. */
5
+ category?: 'implements' | 'engines' | 'curricula' | 'temperaments';
6
+ }
7
+ export interface RemoveResult {
8
+ category: 'implements' | 'engines' | 'curricula' | 'temperaments';
9
+ name: string;
10
+ slot: string;
11
+ removedFrom: string;
12
+ }
13
+ /**
14
+ * Remove a tool from the guild — deregister from guild.json and delete from disk.
15
+ *
16
+ * Only guild-managed tools can be removed. Framework (nexus) tools are managed
17
+ * by `nexus repair` / `nexus install`.
18
+ */
19
+ export declare function removeTool(opts: RemoveToolOptions): RemoveResult;
20
+ //# sourceMappingURL=remove-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-tool.d.ts","sourceRoot":"","sources":["../src/remove-tool.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC;CACpE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAoDhE"}
@@ -0,0 +1,64 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { guildhallWorktreePath } from "./nexus-home.js";
5
+ import { readGuildConfig, writeGuildConfig } from "./guild-config.js";
6
+ const DIR_MAP = {
7
+ implements: 'implements',
8
+ engines: 'engines',
9
+ curricula: 'training/curricula',
10
+ temperaments: 'training/temperaments',
11
+ };
12
+ /** The registries in guild.json that can contain tools, in search order. */
13
+ const REGISTRIES = ['implements', 'engines', 'curricula', 'temperaments'];
14
+ function git(args, cwd) {
15
+ execFileSync('git', args, { cwd, stdio: 'pipe' });
16
+ }
17
+ /**
18
+ * Remove a tool from the guild — deregister from guild.json and delete from disk.
19
+ *
20
+ * Only guild-managed tools can be removed. Framework (nexus) tools are managed
21
+ * by `nexus repair` / `nexus install`.
22
+ */
23
+ export function removeTool(opts) {
24
+ const { home, name } = opts;
25
+ const worktree = guildhallWorktreePath(home);
26
+ const config = readGuildConfig(home);
27
+ // Find the tool in guild.json
28
+ const searchIn = opts.category ? [opts.category] : REGISTRIES;
29
+ let foundCategory;
30
+ for (const cat of searchIn) {
31
+ if (config[cat][name]) {
32
+ foundCategory = cat;
33
+ break;
34
+ }
35
+ }
36
+ if (!foundCategory) {
37
+ throw new Error(`Tool "${name}" not found in guild.json.`);
38
+ }
39
+ const entry = config[foundCategory][name];
40
+ const slot = 'slot' in entry ? entry.slot : '';
41
+ // Prevent removal of framework tools
42
+ if ('source' in entry && entry.source === 'nexus') {
43
+ throw new Error(`"${name}" is a framework tool (source: nexus). Use "nexus repair" to manage framework tools.`);
44
+ }
45
+ // Remove on-disk directory (the specific slot)
46
+ const parentDir = DIR_MAP[foundCategory];
47
+ const toolDir = path.join(worktree, parentDir, name, slot);
48
+ const toolParent = path.join(worktree, parentDir, name);
49
+ if (fs.existsSync(toolDir)) {
50
+ fs.rmSync(toolDir, { recursive: true });
51
+ }
52
+ // Remove the parent name directory if now empty
53
+ if (fs.existsSync(toolParent) && fs.readdirSync(toolParent).length === 0) {
54
+ fs.rmdirSync(toolParent);
55
+ }
56
+ // Deregister from guild.json
57
+ delete config[foundCategory][name];
58
+ writeGuildConfig(home, config);
59
+ // Commit
60
+ git(['add', '-A'], worktree);
61
+ git(['commit', '-m', `Remove ${foundCategory.slice(0, -1)} ${name}`], worktree);
62
+ return { category: foundCategory, name, slot, removedFrom: toolDir };
63
+ }
64
+ //# sourceMappingURL=remove-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-tool.js","sourceRoot":"","sources":["../src/remove-tool.ts"],"names":[],"mappings":"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"}