godpowers 2.2.1 → 2.3.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +33 -7
  2. package/INSPIRATION.md +4 -2
  3. package/README.md +25 -12
  4. package/RELEASE.md +23 -19
  5. package/SKILL.md +2 -2
  6. package/agents/god-auditor.md +1 -1
  7. package/agents/god-deps-auditor.md +11 -0
  8. package/agents/god-executor.md +22 -5
  9. package/agents/god-greenfieldifier.md +1 -1
  10. package/agents/god-orchestrator.md +2 -2
  11. package/agents/god-planner.md +14 -0
  12. package/agents/god-pm.md +1 -1
  13. package/agents/god-reconciler.md +1 -1
  14. package/agents/god-roadmapper.md +1 -1
  15. package/agents/god-spec-reviewer.md +10 -0
  16. package/agents/god-stack-selector.md +4 -0
  17. package/agents/god-updater.md +1 -1
  18. package/bin/install.js +9 -3
  19. package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/manifest.json +2 -2
  20. package/lib/README.md +7 -1
  21. package/lib/atomic-write.js +85 -0
  22. package/lib/checkpoint.js +2 -1
  23. package/lib/context-writer.js +1 -1
  24. package/lib/executor-repair.js +65 -0
  25. package/lib/feature-awareness.js +1 -1
  26. package/lib/fs-async.js +2 -1
  27. package/lib/install-profiles.js +154 -0
  28. package/lib/installer-args.js +12 -0
  29. package/lib/installer-core.js +43 -8
  30. package/lib/linkage.js +2 -1
  31. package/lib/package-identity.js +38 -0
  32. package/lib/package-legitimacy.js +158 -0
  33. package/lib/planning-systems.js +9 -9
  34. package/lib/repo-surface-sync.js +1 -1
  35. package/lib/requirements.js +2 -1
  36. package/lib/reverse-sync.js +2 -1
  37. package/lib/source-grounding.js +177 -0
  38. package/lib/source-sync.js +6 -5
  39. package/lib/state.js +2 -1
  40. package/package.json +1 -1
  41. package/references/HAVE-NOTS.md +1 -1
  42. package/references/orchestration/MODE-DETECTION.md +2 -2
  43. package/references/shared/ORCHESTRATORS.md +3 -3
  44. package/routing/god-design.yaml +1 -1
  45. package/routing/god-migrate.yaml +3 -3
  46. package/schema/state.v1.json +1 -1
  47. package/skills/god-build.md +17 -3
  48. package/skills/god-doctor.md +2 -2
  49. package/skills/god-dogfood.md +2 -2
  50. package/skills/god-init.md +6 -6
  51. package/skills/god-migrate.md +10 -10
  52. package/skills/god-sync.md +2 -2
  53. package/skills/god-update-deps.md +4 -2
  54. package/skills/god-version.md +1 -1
  55. package/templates/IMPORTED-CONTEXT.md +1 -1
  56. package/templates/INITIAL-FINDINGS.md +2 -2
  57. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/PROJECT.md +0 -0
  58. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/REQUIREMENTS.md +0 -0
  59. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/ROADMAP.md +0 -0
@@ -0,0 +1,85 @@
1
+ const fs = require('fs');
2
+ const fsp = require('fs/promises');
3
+ const path = require('path');
4
+
5
+ function tempPathFor(filePath) {
6
+ const dir = path.dirname(filePath);
7
+ const base = path.basename(filePath);
8
+ return path.join(dir, `.${base}.${process.pid}.${Date.now()}.tmp`);
9
+ }
10
+
11
+ function validateContent(content, validate) {
12
+ if (typeof validate === 'function') validate(content);
13
+ }
14
+
15
+ function writeFileAtomic(filePath, content, opts = {}) {
16
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
17
+ const tmp = tempPathFor(filePath);
18
+ try {
19
+ fs.writeFileSync(tmp, content);
20
+ validateContent(content, opts.validateContent);
21
+ if (typeof opts.validateFile === 'function') opts.validateFile(tmp);
22
+ fs.renameSync(tmp, filePath);
23
+ return filePath;
24
+ } catch (error) {
25
+ try {
26
+ fs.rmSync(tmp, { force: true });
27
+ } catch (_) {
28
+ // Best-effort cleanup. The original file is still untouched.
29
+ }
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ async function writeFileAtomicAsync(filePath, content, opts = {}) {
35
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
36
+ const tmp = tempPathFor(filePath);
37
+ try {
38
+ await fsp.writeFile(tmp, content);
39
+ validateContent(content, opts.validateContent);
40
+ if (typeof opts.validateFile === 'function') await opts.validateFile(tmp);
41
+ await fsp.rename(tmp, filePath);
42
+ return filePath;
43
+ } catch (error) {
44
+ try {
45
+ await fsp.rm(tmp, { force: true });
46
+ } catch (_) {
47
+ // Best-effort cleanup. The original file is still untouched.
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+
53
+ function jsonContent(value) {
54
+ return JSON.stringify(value, null, 2) + '\n';
55
+ }
56
+
57
+ function writeJsonAtomic(filePath, value, opts = {}) {
58
+ const content = jsonContent(value);
59
+ return writeFileAtomic(filePath, content, {
60
+ ...opts,
61
+ validateContent: text => {
62
+ JSON.parse(text);
63
+ validateContent(text, opts.validateContent);
64
+ }
65
+ });
66
+ }
67
+
68
+ async function writeJsonAtomicAsync(filePath, value, opts = {}) {
69
+ const content = jsonContent(value);
70
+ return writeFileAtomicAsync(filePath, content, {
71
+ ...opts,
72
+ validateContent: text => {
73
+ JSON.parse(text);
74
+ validateContent(text, opts.validateContent);
75
+ }
76
+ });
77
+ }
78
+
79
+ module.exports = {
80
+ tempPathFor,
81
+ writeFileAtomic,
82
+ writeFileAtomicAsync,
83
+ writeJsonAtomic,
84
+ writeJsonAtomicAsync
85
+ };
package/lib/checkpoint.js CHANGED
@@ -49,6 +49,7 @@
49
49
  const fs = require('fs');
50
50
  const path = require('path');
51
51
  const crypto = require('crypto');
52
+ const atomic = require('./atomic-write');
52
53
 
53
54
  const MAX_ACTIONS = 20;
54
55
  const MAX_FACTS = 10;
@@ -224,7 +225,7 @@ function write(projectRoot, state) {
224
225
  ''
225
226
  ].filter(line => line !== null).join('\n');
226
227
 
227
- fs.writeFileSync(file, fm + body);
228
+ atomic.writeFileAtomic(file, fm + body);
228
229
  return file;
229
230
  }
230
231
 
@@ -99,7 +99,7 @@ function buildCanonicalContent(state, opts = {}) {
99
99
  lines.push('- `/god-next` - what to run next, with reason');
100
100
  lines.push('- `/god-mode` - run the full autonomous project run');
101
101
  lines.push('- `/god-sync` - refresh artifacts, context, and source-system sync-back');
102
- lines.push('- `/god-migrate` - import or sync GSD, BMAD, or Superpowers context');
102
+ lines.push('- `/god-migrate` - import or sync legacy planning, BMAD, or Superpowers context');
103
103
  lines.push('- `/god-context refresh` - refresh AI-tool awareness for this project');
104
104
  lines.push('');
105
105
 
@@ -0,0 +1,65 @@
1
+ const STRATEGIES = Object.freeze({
2
+ RETRY: 'retry',
3
+ DECOMPOSE: 'decompose',
4
+ PRUNE: 'prune',
5
+ ESCALATE: 'escalate'
6
+ });
7
+
8
+ function classifyFailure(input = {}) {
9
+ const attempts = Number(input.attempts || 0);
10
+ const budget = Number(Object.prototype.hasOwnProperty.call(input, 'budget') ? input.budget : 2);
11
+ const error = String(input.error || '').toLowerCase();
12
+ const criteria = String(input.doneCriteria || '').toLowerCase();
13
+
14
+ if (attempts >= budget) {
15
+ return {
16
+ strategy: STRATEGIES.ESCALATE,
17
+ reason: 'repair budget exhausted'
18
+ };
19
+ }
20
+
21
+ if (/architecture|product decision|ambiguous|human/.test(error)) {
22
+ return {
23
+ strategy: STRATEGIES.ESCALATE,
24
+ reason: 'failure requires a human or architecture decision'
25
+ };
26
+ }
27
+
28
+ if (/not found|enoent|missing dependency|cannot find module|wrong path|permission|timeout|network|econn/.test(error)) {
29
+ return {
30
+ strategy: STRATEGIES.RETRY,
31
+ reason: 'failure looks environmental or mechanical'
32
+ };
33
+ }
34
+
35
+ if (/and|multiple|all of|end-to-end|full flow/.test(criteria) || /too broad|partial|only.*part/.test(error)) {
36
+ return {
37
+ strategy: STRATEGIES.DECOMPOSE,
38
+ reason: 'done criteria appears too broad for one verified step'
39
+ };
40
+ }
41
+
42
+ if (/out of scope|blocked by missing prerequisite|unsupported|cannot be implemented/.test(error)) {
43
+ return {
44
+ strategy: STRATEGIES.PRUNE,
45
+ reason: 'task appears infeasible in the current slice'
46
+ };
47
+ }
48
+
49
+ return {
50
+ strategy: STRATEGIES.RETRY,
51
+ reason: 'default to one focused retry before broader action'
52
+ };
53
+ }
54
+
55
+ function renderRepairLog(task, decision) {
56
+ const name = task || 'task';
57
+ const strategy = decision.strategy.toUpperCase();
58
+ return `[Executor Repair - ${strategy}] ${name}: ${decision.reason}`;
59
+ }
60
+
61
+ module.exports = {
62
+ STRATEGIES,
63
+ classifyFailure,
64
+ renderRepairLog
65
+ };
@@ -21,7 +21,7 @@ const FEATURES = [
21
21
  id: 'planning-system-migration',
22
22
  since: '1.6.15',
23
23
  commands: ['/god-migrate', '/god-init'],
24
- description: 'Detect and import GSD, BMAD, and Superpowers planning artifacts.'
24
+ description: 'Detect and import legacy planning, BMAD, and Superpowers planning artifacts.'
25
25
  },
26
26
  {
27
27
  id: 'source-system-sync-back',
package/lib/fs-async.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs/promises');
2
2
  const path = require('path');
3
+ const atomic = require('./atomic-write');
3
4
 
4
5
  async function exists(filePath) {
5
6
  try {
@@ -16,7 +17,7 @@ async function readJson(filePath) {
16
17
 
17
18
  async function writeJson(filePath, value) {
18
19
  await fs.mkdir(path.dirname(filePath), { recursive: true });
19
- await fs.writeFile(filePath, JSON.stringify(value, null, 2) + '\n');
20
+ await atomic.writeJsonAtomicAsync(filePath, value);
20
21
  return value;
21
22
  }
22
23
 
@@ -0,0 +1,154 @@
1
+ const COMMON = [
2
+ 'god',
3
+ 'god-help',
4
+ 'god-version',
5
+ 'god-next',
6
+ 'god-status',
7
+ 'god-progress',
8
+ 'god-doctor',
9
+ 'god-settings'
10
+ ];
11
+
12
+ const PROFILE_SKILLS = {
13
+ core: [
14
+ ...COMMON,
15
+ 'god-init',
16
+ 'god-mode',
17
+ 'god-build',
18
+ 'god-review',
19
+ 'god-sync',
20
+ 'god-quick',
21
+ 'god-fast'
22
+ ],
23
+ builder: [
24
+ ...COMMON,
25
+ 'god-init',
26
+ 'god-mode',
27
+ 'god-discuss',
28
+ 'god-explore',
29
+ 'god-list-assumptions',
30
+ 'god-prd',
31
+ 'god-design',
32
+ 'god-design-impact',
33
+ 'god-arch',
34
+ 'god-roadmap',
35
+ 'god-stack',
36
+ 'god-repo',
37
+ 'god-build',
38
+ 'god-add-tests',
39
+ 'god-feature',
40
+ 'god-story',
41
+ 'god-stories',
42
+ 'god-story-build',
43
+ 'god-story-verify',
44
+ 'god-story-close',
45
+ 'god-review',
46
+ 'god-test-runtime',
47
+ 'god-sync',
48
+ 'god-quick',
49
+ 'god-fast'
50
+ ],
51
+ maintainer: [
52
+ ...COMMON,
53
+ 'god-hygiene',
54
+ 'god-update-deps',
55
+ 'god-docs',
56
+ 'god-repair',
57
+ 'god-lint',
58
+ 'god-standards',
59
+ 'god-preflight',
60
+ 'god-audit',
61
+ 'god-agent-audit',
62
+ 'god-context',
63
+ 'god-context-scan',
64
+ 'god-locate',
65
+ 'god-scan',
66
+ 'god-link',
67
+ 'god-review-changes',
68
+ 'god-reconcile',
69
+ 'god-reconstruct',
70
+ 'god-migrate',
71
+ 'god-automation-status',
72
+ 'god-automation-setup',
73
+ 'god-extension-add',
74
+ 'god-extension-list',
75
+ 'god-extension-info',
76
+ 'god-extension-remove',
77
+ 'god-test-extension',
78
+ 'god-budget',
79
+ 'god-cost',
80
+ 'god-cache-clear',
81
+ 'god-logs',
82
+ 'god-metrics',
83
+ 'god-trace',
84
+ 'god-export-otel',
85
+ 'god-dogfood',
86
+ 'god-quick',
87
+ 'god-fast'
88
+ ],
89
+ suite: [
90
+ ...COMMON,
91
+ 'god-suite-init',
92
+ 'god-suite-status',
93
+ 'god-suite-sync',
94
+ 'god-suite-patch',
95
+ 'god-suite-release',
96
+ 'god-workstream',
97
+ 'god-pr-branch',
98
+ 'god-sync',
99
+ 'god-reconcile',
100
+ 'god-review',
101
+ 'god-quick',
102
+ 'god-fast'
103
+ ]
104
+ };
105
+
106
+ const PROFILE_DESCRIPTIONS = {
107
+ core: 'front door, status, init, build, review, sync, quick edits',
108
+ builder: 'core plus planning, design, stories, and runtime verification',
109
+ maintainer: 'core plus hygiene, deps, docs, repair, automation, and extensions',
110
+ suite: 'core plus multi-repo suite and workstream coordination',
111
+ full: 'all shipped slash commands'
112
+ };
113
+
114
+ function normalizeProfiles(value) {
115
+ if (!value) return ['full'];
116
+ const raw = String(value)
117
+ .split(',')
118
+ .map(part => part.trim().toLowerCase())
119
+ .filter(Boolean);
120
+ const profiles = raw.length > 0 ? raw : ['full'];
121
+ for (const profile of profiles) {
122
+ if (profile !== 'full' && !PROFILE_SKILLS[profile]) {
123
+ throw new Error(`Unknown install profile: ${profile}`);
124
+ }
125
+ }
126
+ if (profiles.includes('full')) return ['full'];
127
+ return [...new Set(profiles)];
128
+ }
129
+
130
+ function selectedSkillNames(profileValue, availableNames) {
131
+ const profiles = normalizeProfiles(profileValue);
132
+ if (profiles.includes('full')) return new Set(availableNames);
133
+ const selected = new Set();
134
+ for (const profile of profiles) {
135
+ for (const name of PROFILE_SKILLS[profile]) {
136
+ if (availableNames.includes(name)) selected.add(name);
137
+ }
138
+ }
139
+ return selected;
140
+ }
141
+
142
+ function describeProfiles(profileValue) {
143
+ return normalizeProfiles(profileValue)
144
+ .map(profile => `${profile}: ${PROFILE_DESCRIPTIONS[profile]}`)
145
+ .join('; ');
146
+ }
147
+
148
+ module.exports = {
149
+ PROFILE_SKILLS,
150
+ PROFILE_DESCRIPTIONS,
151
+ normalizeProfiles,
152
+ selectedSkillNames,
153
+ describeProfiles
154
+ };
@@ -29,6 +29,7 @@ function parseArgs(argv, cwd = process.cwd()) {
29
29
  all: false,
30
30
  help: false,
31
31
  uninstall: false,
32
+ profile: 'full',
32
33
  };
33
34
 
34
35
  for (let i = 0; i < args.length; i++) {
@@ -62,6 +63,15 @@ function parseArgs(argv, cwd = process.cwd()) {
62
63
  case '--all':
63
64
  opts.all = true;
64
65
  break;
66
+ case '--minimal':
67
+ opts.profile = 'core';
68
+ break;
69
+ case '--profile':
70
+ if (args[i + 1]) {
71
+ opts.profile = args[i + 1];
72
+ i++;
73
+ }
74
+ break;
65
75
  case '-h':
66
76
  case '--help':
67
77
  opts.help = true;
@@ -83,6 +93,8 @@ function parseArgs(argv, cwd = process.cwd()) {
83
93
  opts.extensionAgent = arg.slice('--agent='.length);
84
94
  } else if (arg.startsWith('--workflow=')) {
85
95
  opts.extensionWorkflow = arg.slice('--workflow='.length);
96
+ } else if (arg.startsWith('--profile=')) {
97
+ opts.profile = arg.slice('--profile='.length);
86
98
  } else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
87
99
  opts.runtimes.push(arg.slice(2));
88
100
  }
@@ -3,8 +3,10 @@ const path = require('path');
3
3
 
4
4
  const { ensureDir, copyRecursive, copyRuntimeBundle } = require('./installer-files');
5
5
  const { resolveRuntime } = require('./installer-runtimes');
6
+ const { selectedSkillNames, normalizeProfiles } = require('./install-profiles');
7
+ const identity = require('./package-identity');
6
8
 
7
- const VERSION = require('../package.json').version;
9
+ const VERSION = identity.PACKAGE_VERSION;
8
10
 
9
11
  /**
10
12
  * @typedef {Object} InstallOptions
@@ -141,6 +143,15 @@ function removeSkillEntry(skillsDir, entry) {
141
143
  return false;
142
144
  }
143
145
 
146
+ function pruneGodpowersSkills(skillsDir) {
147
+ let removed = 0;
148
+ if (!fs.existsSync(skillsDir)) return removed;
149
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
150
+ if (removeSkillEntry(skillsDir, entry)) removed++;
151
+ }
152
+ return removed;
153
+ }
154
+
144
155
  function installForRuntime(runtimeKey, srcDir, opts = {}) {
145
156
  const runtime = resolveRuntime(runtimeKey, opts);
146
157
  if (!runtime) {
@@ -151,7 +162,7 @@ function installForRuntime(runtimeKey, srcDir, opts = {}) {
151
162
  log(`\n Installing for \x1b[36m${runtime.name}\x1b[0m to \x1b[36m${runtime.configDir}\x1b[0m\n`);
152
163
  ensureDir(runtime.configDir);
153
164
 
154
- installSkills(srcDir, runtimeKey, runtime);
165
+ installSkills(srcDir, runtimeKey, runtime, opts);
155
166
  installAgents(srcDir, runtime);
156
167
  installMasterSkill(srcDir, runtimeKey, runtime);
157
168
  installDataDirs(srcDir, runtime);
@@ -159,18 +170,33 @@ function installForRuntime(runtimeKey, srcDir, opts = {}) {
159
170
 
160
171
  fs.writeFileSync(path.join(runtime.configDir, 'GODPOWERS_VERSION'), VERSION);
161
172
  success(`Wrote GODPOWERS_VERSION (${VERSION})`);
173
+ fs.writeFileSync(path.join(runtime.configDir, 'GODPOWERS_PROFILE'), normalizeProfiles(opts.profile).join(','));
174
+ success(`Wrote GODPOWERS_PROFILE (${normalizeProfiles(opts.profile).join(',')})`);
162
175
  return true;
163
176
  }
164
177
 
165
- function installSkills(srcDir, runtimeKey, runtime) {
178
+ function availableSkillFiles(srcDir) {
179
+ const skillsSrc = path.join(srcDir, 'skills');
180
+ if (!fs.existsSync(skillsSrc)) return [];
181
+ return fs.readdirSync(skillsSrc)
182
+ .filter(file => file.endsWith('.md'))
183
+ .sort();
184
+ }
185
+
186
+ function installSkills(srcDir, runtimeKey, runtime, opts = {}) {
166
187
  const skillsSrc = path.join(srcDir, 'skills');
167
188
  const skillsDest = path.join(runtime.configDir, runtime.skillsDir);
168
189
  if (!fs.existsSync(skillsSrc)) return;
169
190
 
170
191
  ensureDir(skillsDest);
192
+ pruneGodpowersSkills(skillsDest);
193
+ const files = availableSkillFiles(srcDir);
194
+ const names = files.map(file => path.basename(file, '.md'));
195
+ const selected = selectedSkillNames(opts.profile, names);
171
196
  let count = 0;
172
- for (const file of fs.readdirSync(skillsSrc)) {
173
- if (file.endsWith('.md')) {
197
+ for (const file of files) {
198
+ const name = path.basename(file, '.md');
199
+ if (selected.has(name)) {
174
200
  installSkillFile(path.join(skillsSrc, file), skillsDest, runtimeKey);
175
201
  count++;
176
202
  }
@@ -327,8 +353,15 @@ function uninstallHooks(runtimeKey, runtime) {
327
353
  }
328
354
 
329
355
  function countInstalledSurface(srcDir) {
356
+ return countProfileSurface(srcDir, { profile: 'full' });
357
+ }
358
+
359
+ function countProfileSurface(srcDir, opts = {}) {
360
+ const files = availableSkillFiles(srcDir);
361
+ const names = files.map(file => path.basename(file, '.md'));
362
+ const selected = selectedSkillNames(opts.profile, names);
330
363
  return {
331
- skills: fs.readdirSync(path.join(srcDir, 'skills')).filter(f => f.endsWith('.md')).length,
364
+ skills: selected.size,
332
365
  agents: fs.readdirSync(path.join(srcDir, 'agents')).filter(f => /^god-.*\.md$/.test(f)).length
333
366
  };
334
367
  }
@@ -336,10 +369,12 @@ function countInstalledSurface(srcDir) {
336
369
  module.exports = {
337
370
  installForRuntime,
338
371
  uninstallForRuntime,
339
- countInstalledSurface,
372
+ countInstalledSurface: countProfileSurface,
340
373
  installSkillFile,
341
374
  parseAgentFrontmatter,
342
375
  stripFrontmatter,
343
376
  writeCodexAgentToml,
344
- removeSkillEntry
377
+ removeSkillEntry,
378
+ pruneGodpowersSkills,
379
+ installSkills
345
380
  };
package/lib/linkage.js CHANGED
@@ -23,6 +23,7 @@
23
23
 
24
24
  const fs = require('fs');
25
25
  const path = require('path');
26
+ const atomic = require('./atomic-write');
26
27
 
27
28
  const ID_PATTERNS = {
28
29
  prd: /^P-(MUST|SHOULD|COULD)-\d+$/,
@@ -77,7 +78,7 @@ function readMap(filePath) {
77
78
  }
78
79
 
79
80
  function writeMap(filePath, data) {
80
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
81
+ atomic.writeJsonAtomic(filePath, data);
81
82
  }
82
83
 
83
84
  function linkKey(artifactId, filePath) {
@@ -0,0 +1,38 @@
1
+ const pkg = require('../package.json');
2
+
3
+ const PACKAGE_NAME = pkg.name;
4
+ const PACKAGE_VERSION = pkg.version;
5
+ const REPOSITORY_URL = pkg.repository && pkg.repository.url
6
+ ? pkg.repository.url
7
+ : 'git+https://github.com/aihxp/godpowers.git';
8
+ const HOMEPAGE_URL = pkg.homepage || 'https://github.com/aihxp/godpowers#readme';
9
+ const BUGS_URL = pkg.bugs && pkg.bugs.url
10
+ ? pkg.bugs.url
11
+ : 'https://github.com/aihxp/godpowers/issues';
12
+ const BIN_NAME = pkg.bin && Object.keys(pkg.bin)[0]
13
+ ? Object.keys(pkg.bin)[0]
14
+ : 'godpowers';
15
+
16
+ function repoSlug() {
17
+ const url = REPOSITORY_URL
18
+ .replace(/^git\+/, '')
19
+ .replace(/^https:\/\/github\.com\//, '')
20
+ .replace(/^git@github\.com:/, '')
21
+ .replace(/\.git$/, '');
22
+ return url || 'aihxp/godpowers';
23
+ }
24
+
25
+ function npxCommand(version = 'latest') {
26
+ return `npx ${PACKAGE_NAME}@${version}`;
27
+ }
28
+
29
+ module.exports = {
30
+ PACKAGE_NAME,
31
+ PACKAGE_VERSION,
32
+ REPOSITORY_URL,
33
+ HOMEPAGE_URL,
34
+ BUGS_URL,
35
+ BIN_NAME,
36
+ repoSlug,
37
+ npxCommand
38
+ };