@loopress/cli 0.6.0 → 0.7.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 (35) hide show
  1. package/README.md +71 -90
  2. package/dist/commands/composer/pull.js +1 -1
  3. package/dist/commands/composer/push.js +1 -1
  4. package/dist/commands/init.js +15 -6
  5. package/dist/commands/login.js +1 -1
  6. package/dist/commands/logout.js +1 -1
  7. package/dist/commands/plugin/add.d.ts +0 -2
  8. package/dist/commands/plugin/add.js +2 -23
  9. package/dist/commands/plugin/pull.js +1 -1
  10. package/dist/commands/plugin/push.js +3 -3
  11. package/dist/commands/project/config.d.ts +1 -0
  12. package/dist/commands/project/config.js +36 -17
  13. package/dist/commands/project/list.js +4 -5
  14. package/dist/commands/project/remove.js +33 -14
  15. package/dist/commands/project/switch.d.ts +2 -0
  16. package/dist/commands/project/switch.js +28 -9
  17. package/dist/commands/snippet/list.js +3 -3
  18. package/dist/commands/snippet/pull.d.ts +1 -1
  19. package/dist/commands/snippet/pull.js +7 -6
  20. package/dist/commands/snippet/push.d.ts +2 -2
  21. package/dist/commands/snippet/push.js +47 -15
  22. package/dist/commands/{project/remove-env.d.ts → status.d.ts} +3 -1
  23. package/dist/commands/status.js +66 -0
  24. package/dist/config/project-config.manager.d.ts +17 -10
  25. package/dist/config/project-config.manager.js +91 -44
  26. package/dist/config/types.d.ts +5 -2
  27. package/dist/lib/base.js +13 -3
  28. package/dist/types/snippet.d.ts +2 -0
  29. package/dist/utils/snippet-plugin.d.ts +2 -1
  30. package/dist/utils/snippet-plugin.js +5 -4
  31. package/oclif.manifest.json +149 -175
  32. package/package.json +17 -2
  33. package/dist/commands/project/remove-env.js +0 -33
  34. package/dist/commands/project/switch-env.d.ts +0 -6
  35. package/dist/commands/project/switch-env.js +0 -33
@@ -12,17 +12,16 @@ export default class List extends Command {
12
12
  return;
13
13
  }
14
14
  for (const project of projects) {
15
- const envs = Object.values(project.environments);
15
+ const envs = configManager.listEnvironments(project.id);
16
16
  const marker = project.isCurrent ? c('green', '●') : c('dim', '○');
17
17
  const name = project.isCurrent ? c('green', project.name) : project.name;
18
18
  const currentTag = project.isCurrent ? ` ${c('green', '[current]')}` : '';
19
19
  this.log(`${marker} ${name}${currentTag}`);
20
20
  for (const env of envs) {
21
- const isActiveEnv = env.name === project.currentEnv;
22
- const envMarker = isActiveEnv ? c('cyan', '·') : c('dim', '·');
23
- const envName = isActiveEnv ? c('cyan', env.name.padEnd(15)) : c('dim', env.name.padEnd(15));
21
+ const envMarker = env.isCurrent ? c('cyan', '·') : c('dim', '·');
22
+ const envName = env.isCurrent ? c('cyan', env.name.padEnd(15)) : c('dim', env.name.padEnd(15));
24
23
  const envUrl = c('dim', env.url);
25
- const activeTag = isActiveEnv ? ` ${c('cyan', '←')}` : '';
24
+ const activeTag = env.isCurrent ? ` ${c('cyan', '←')}` : '';
26
25
  this.log(` ${envMarker} ${envName} ${envUrl}${activeTag}`);
27
26
  }
28
27
  this.log('');
@@ -2,7 +2,7 @@ import { checkbox } from '@inquirer/prompts';
2
2
  import { Command } from '@oclif/core';
3
3
  import { configManager } from '../../config/project-config.manager.js';
4
4
  export default class Remove extends Command {
5
- static description = 'Remove one or more WordPress project configurations';
5
+ static description = 'Remove one or more WordPress projects or environments';
6
6
  static examples = ['$ lps project remove'];
7
7
  async run() {
8
8
  await this.parse(Remove);
@@ -10,25 +10,44 @@ export default class Remove extends Command {
10
10
  if (projects.length === 0) {
11
11
  this.error('No projects configured.');
12
12
  }
13
- const chosen = await checkbox({
14
- choices: projects.map((project) => {
15
- const envCount = Object.keys(project.environments).length;
16
- const envLabel = `${envCount} env${envCount > 1 ? 's' : ''}`;
17
- const currentMarker = project.isCurrent ? ' [current]' : '';
13
+ const targets = [];
14
+ const choices = projects.flatMap((project) => {
15
+ const envCount = Object.keys(project.environments).length;
16
+ const envLabel = `${envCount} env${envCount > 1 ? 's' : ''}`;
17
+ const currentMarker = project.isCurrent ? ' [current]' : '';
18
+ targets.push({ kind: 'project', projectId: project.id, projectName: project.name });
19
+ const projectChoice = {
20
+ name: `${project.isCurrent ? '●' : '○'} ${project.name.padEnd(20)} (${envLabel})${currentMarker}`,
21
+ value: String(targets.length - 1),
22
+ };
23
+ const envChoices = configManager.listEnvironments(project.id).map((env) => {
24
+ targets.push({ env: env.name, kind: 'env', projectId: project.id, projectName: project.name });
18
25
  return {
19
- name: `${project.isCurrent ? '●' : '○'} ${project.name.padEnd(20)} (${envLabel})${currentMarker}`,
20
- value: project.name,
26
+ name: ` ${env.isCurrent ? '●' : '○'} ${env.name.padEnd(20)} ${env.url}${env.isCurrent ? ' [current]' : ''}`,
27
+ value: String(targets.length - 1),
21
28
  };
22
- }),
23
- message: 'Select projects to remove',
29
+ });
30
+ return [projectChoice, ...envChoices];
31
+ });
32
+ const chosen = await checkbox({
33
+ choices,
34
+ message: 'Select projects or environments to remove',
24
35
  });
25
36
  if (chosen.length === 0) {
26
37
  this.log('Nothing removed.');
27
38
  return;
28
39
  }
29
- for (const name of chosen) {
30
- configManager.removeProject(name);
31
- }
32
- this.log(`✓ Removed: ${chosen.join(', ')}`);
40
+ const selected = chosen.map((index) => targets[Number(index)]);
41
+ const projectsToRemove = new Map(selected.filter((target) => target.kind === 'project').map((target) => [target.projectId, target.projectName]));
42
+ const envsToRemove = selected.filter((target) => target.kind === 'env' && !projectsToRemove.has(target.projectId));
43
+ for (const projectId of projectsToRemove.keys())
44
+ configManager.removeProject(projectId);
45
+ for (const { env, projectId } of envsToRemove)
46
+ configManager.removeEnvironment(projectId, env);
47
+ const removedLabels = [
48
+ ...projectsToRemove.values(),
49
+ ...envsToRemove.map(({ env, projectName }) => `${projectName}/${env}`),
50
+ ];
51
+ this.log(`✓ Removed: ${removedLabels.join(', ')}`);
33
52
  }
34
53
  }
@@ -3,4 +3,6 @@ export default class Switch extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  run(): Promise<void>;
6
+ private resolveEnvironment;
7
+ private resolveProject;
6
8
  }
@@ -2,7 +2,7 @@ import { select } from '@inquirer/prompts';
2
2
  import { Command } from '@oclif/core';
3
3
  import { configManager } from '../../config/project-config.manager.js';
4
4
  export default class Switch extends Command {
5
- static description = 'Switch the active project';
5
+ static description = 'Switch the active project and environment';
6
6
  static examples = ['$ lps project switch'];
7
7
  async run() {
8
8
  await this.parse(Switch);
@@ -10,11 +10,30 @@ export default class Switch extends Command {
10
10
  if (projects.length === 0) {
11
11
  this.error('No projects configured. Run `lps project config` first.');
12
12
  }
13
- if (projects.length === 1) {
14
- configManager.setCurrentProject(projects[0].name);
15
- this.log(`✓ Switched to "${projects[0].name}"`);
16
- return;
13
+ const { id: projectId, name: projectName } = await this.resolveProject(projects);
14
+ const envName = await this.resolveEnvironment(projectId, projectName);
15
+ configManager.setCurrent(projectId, envName);
16
+ this.log(`✓ Switched to "${projectName}/${envName}"`);
17
+ }
18
+ async resolveEnvironment(projectId, projectName) {
19
+ const envs = configManager.listEnvironments(projectId);
20
+ if (envs.length === 0) {
21
+ this.error(`No environments configured for "${projectName}". Run \`lps project config\` first.`);
17
22
  }
23
+ if (envs.length === 1)
24
+ return envs[0].name;
25
+ return select({
26
+ choices: envs.map((env) => ({
27
+ name: `${env.isCurrent ? '●' : '○'} ${env.name.padEnd(20)} ${env.url}${env.isCurrent ? ' [current]' : ''}`,
28
+ value: env.name,
29
+ })),
30
+ default: envs.find((env) => env.isCurrent)?.name,
31
+ message: `Select environment for "${projectName}"`,
32
+ });
33
+ }
34
+ async resolveProject(projects) {
35
+ if (projects.length === 1)
36
+ return { id: projects[0].id, name: projects[0].name };
18
37
  const chosen = await select({
19
38
  choices: projects.map((project) => {
20
39
  const envCount = Object.keys(project.environments).length;
@@ -22,13 +41,13 @@ export default class Switch extends Command {
22
41
  const currentMarker = project.isCurrent ? ' [current]' : '';
23
42
  return {
24
43
  name: `${project.isCurrent ? '●' : '○'} ${project.name.padEnd(20)} (${envLabel})${currentMarker}`,
25
- value: project.name,
44
+ value: project.id,
26
45
  };
27
46
  }),
28
- default: projects.find((p) => p.isCurrent)?.name,
47
+ default: projects.find((project) => project.isCurrent)?.id,
29
48
  message: 'Select active project',
30
49
  });
31
- configManager.setCurrentProject(chosen);
32
- this.log(`✓ Switched to "${chosen}"`);
50
+ const project = projects.find((p) => p.id === chosen);
51
+ return { id: project.id, name: project.name };
33
52
  }
34
53
  }
@@ -5,9 +5,9 @@ import { getSnippetPlugin } from '../../utils/snippet-plugin.js';
5
5
  export default class List extends LoopressCommand {
6
6
  static description = 'List snippets from WordPress';
7
7
  static examples = [
8
- '$ lps snippets list',
9
- '$ lps snippets list --url http://example.com',
10
- '$ lps snippets list --plugin wpcode',
8
+ '$ lps snippet list',
9
+ '$ lps snippet list --url http://example.com',
10
+ '$ lps snippet list --plugin wpcode',
11
11
  ];
12
12
  static flags = {
13
13
  ...LoopressCommand.baseFlags,
@@ -9,7 +9,7 @@ export default class Pull extends LoopressCommand {
9
9
  static description: string;
10
10
  static examples: string[];
11
11
  static flags: {
12
- dryRun: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
13
  plugin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -35,14 +35,14 @@ export default class Pull extends LoopressCommand {
35
35
  };
36
36
  static description = 'Pull snippets from WordPress';
37
37
  static examples = [
38
- '$ lps snippets pull',
39
- '$ lps snippets pull --url http://example.com',
40
- '$ lps snippets pull --path ./snippets',
41
- '$ lps snippets pull --plugin wpcode',
38
+ '$ lps snippet pull',
39
+ '$ lps snippet pull --url http://example.com',
40
+ '$ lps snippet pull --path ./snippets',
41
+ '$ lps snippet pull --plugin wpcode',
42
42
  ];
43
43
  static flags = {
44
44
  ...LoopressCommand.baseFlags,
45
- dryRun: Flags.boolean({ char: 'd', description: 'Dry run - show what would happen without making changes' }),
45
+ 'dry-run': Flags.boolean({ char: 'd', description: 'Show what would be written without making changes' }),
46
46
  plugin: Flags.string({
47
47
  char: 'p',
48
48
  description: 'WordPress snippet plugin to target (overrides loopress.json)',
@@ -51,7 +51,8 @@ export default class Pull extends LoopressCommand {
51
51
  };
52
52
  async run() {
53
53
  const { args, flags } = await this.parse(Pull);
54
- const { dryRun, plugin } = flags;
54
+ const dryRun = flags['dry-run'];
55
+ const { plugin } = flags;
55
56
  const { url } = this.siteConfig;
56
57
  const path = await this.resolveSnippetsPath(args.path);
57
58
  const resolvedPlugin = await this.resolveSnippetPlugin(plugin);
@@ -6,14 +6,14 @@ export default class Push extends PushCommand {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- dryRun: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  plugin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
13
  user: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  };
15
15
  run(): Promise<void>;
16
- private injectIdIntoMeta;
16
+ private ensureCanonicalFilename;
17
17
  private loadSnippets;
18
18
  private pushSnippet;
19
19
  }
@@ -1,21 +1,30 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import got from 'got';
3
+ import { basename, dirname, extname, join } from 'node:path';
4
+ import slugify from 'slugify';
3
5
  import { PushCommand } from '../../lib/push-command.js';
4
- import { getSnippetPlugin } from '../../utils/snippet-plugin.js';
6
+ import { getSnippetPlugin, parseType } from '../../utils/snippet-plugin.js';
7
+ const TYPE_BY_EXTENSION = {
8
+ '.css': 'css',
9
+ '.html': 'html',
10
+ '.js': 'js',
11
+ '.php': 'php',
12
+ '.txt': 'text',
13
+ };
5
14
  export default class Push extends PushCommand {
6
15
  static args = {
7
16
  path: Args.string({ description: 'Path to snippets directory (overrides project config)' }),
8
17
  };
9
- static description = 'Push snippets to WordPress';
18
+ static description = 'Push snippets to WordPress. Local snippet files created or updated remotely are renamed on disk to the `<id>-<slug>` convention.';
10
19
  static examples = [
11
- '$ lps snippets push',
12
- '$ lps snippets push --url http://example.com',
13
- '$ lps snippets push --path ./snippets',
14
- '$ lps snippets push --plugin wpcode',
20
+ '$ lps snippet push',
21
+ '$ lps snippet push --url http://example.com',
22
+ '$ lps snippet push --path ./snippets',
23
+ '$ lps snippet push --plugin wpcode',
15
24
  ];
16
25
  static flags = {
17
26
  ...PushCommand.baseFlags,
18
- dryRun: Flags.boolean({ char: 'd', description: 'Dry run - show what would happen without making changes' }),
27
+ 'dry-run': Flags.boolean({ char: 'd', description: 'Show what would change without making changes' }),
19
28
  plugin: Flags.string({
20
29
  char: 'p',
21
30
  description: 'WordPress snippet plugin to target (overrides loopress.json)',
@@ -24,7 +33,8 @@ export default class Push extends PushCommand {
24
33
  };
25
34
  async run() {
26
35
  const { args, flags } = await this.parse(Push);
27
- const { dryRun, plugin } = flags;
36
+ const dryRun = flags['dry-run'];
37
+ const { plugin } = flags;
28
38
  this.dryRun = dryRun;
29
39
  const { url } = this.siteConfig;
30
40
  const path = await this.resolveSnippetsPath(args.path);
@@ -48,12 +58,19 @@ export default class Push extends PushCommand {
48
58
  this.error(error.message);
49
59
  }
50
60
  }
51
- async injectIdIntoMeta(filePath, id) {
61
+ // Renames the local file pair to the `<id>-<slug>` convention used by `snippet pull` whenever
62
+ // it doesn't already match (e.g. a hand-created `demo.php` with no id, or a stale slug after a rename).
63
+ // This is a side effect of `push`: local files on disk are renamed, not just the remote snippet.
64
+ async ensureCanonicalFilename(snippet, id, name) {
52
65
  const fs = await import('node:fs/promises');
53
- const metaPath = filePath.replace(/\.[^.]+$/, '.json');
66
+ const dir = dirname(snippet.path);
67
+ const ext = extname(snippet.path);
68
+ const currentBase = basename(snippet.path, ext);
69
+ const canonicalBase = `${id}-${slugify(name, { lower: true, strict: true })}`;
70
+ const oldMetaPath = join(dir, `${currentBase}.json`);
54
71
  let meta = {};
55
72
  try {
56
- const existing = await fs.readFile(metaPath, 'utf8');
73
+ const existing = await fs.readFile(oldMetaPath, 'utf8');
57
74
  meta = JSON.parse(existing);
58
75
  }
59
76
  catch (error) {
@@ -61,12 +78,23 @@ export default class Push extends PushCommand {
61
78
  throw error;
62
79
  }
63
80
  meta.id = id;
64
- await fs.writeFile(metaPath, JSON.stringify(meta, null, 2) + '\n');
81
+ meta.name = name;
82
+ if (currentBase === canonicalBase) {
83
+ await fs.writeFile(oldMetaPath, JSON.stringify(meta, null, 2) + '\n');
84
+ return;
85
+ }
86
+ const newPath = join(dir, `${canonicalBase}${ext}`);
87
+ const newMetaPath = join(dir, `${canonicalBase}.json`);
88
+ await fs.rename(snippet.path, newPath);
89
+ await fs.writeFile(newMetaPath, JSON.stringify(meta, null, 2) + '\n');
90
+ if (oldMetaPath !== newMetaPath)
91
+ await fs.rm(oldMetaPath, { force: true });
92
+ this.log(`📁 Renamed: ${snippet.path} → ${newPath}`);
65
93
  }
66
94
  async loadSnippets(path) {
67
95
  const fs = await import('node:fs/promises');
68
96
  const snippets = [];
69
- const SNIPPET_EXTENSIONS = new Set(['.css', '.html', '.js', '.php', '.txt']);
97
+ const SNIPPET_EXTENSIONS = new Set(Object.keys(TYPE_BY_EXTENSION));
70
98
  try {
71
99
  const files = await fs.readdir(path);
72
100
  for (const file of files) {
@@ -78,11 +106,13 @@ export default class Push extends PushCommand {
78
106
  const content = await fs.readFile(filePath, 'utf8');
79
107
  let id;
80
108
  let name;
109
+ let type;
81
110
  try {
82
111
  const metaContent = await fs.readFile(metaPath, 'utf8');
83
112
  const meta = JSON.parse(metaContent);
84
113
  id = meta.id ? Number(meta.id) : undefined;
85
114
  name = meta.name ? String(meta.name) : undefined;
115
+ type = parseType(meta.type) ?? undefined;
86
116
  }
87
117
  catch (error) {
88
118
  if (error.code !== 'ENOENT')
@@ -93,6 +123,7 @@ export default class Push extends PushCommand {
93
123
  id,
94
124
  name: name ?? file.slice(0, file.lastIndexOf('.')),
95
125
  path: filePath,
126
+ type: type ?? TYPE_BY_EXTENSION[ext] ?? 'php',
96
127
  });
97
128
  }
98
129
  }
@@ -110,18 +141,19 @@ export default class Push extends PushCommand {
110
141
  }
111
142
  try {
112
143
  const endpoint = adapter.endpoint(url);
113
- const payload = adapter.toPayload(snippet.name, snippet.code, snippet.path);
144
+ const payload = adapter.toPayload(snippet.name, snippet.code, snippet.path, snippet.type);
114
145
  if (snippet.id) {
115
146
  this.log(`🔄 Updating snippet by id (${snippet.id}): ${snippet.name}`);
116
147
  await got.put(`${endpoint}/${snippet.id}`, { headers, json: payload });
117
148
  this.log(`✅ Updated: ${snippet.name}`);
149
+ await this.ensureCanonicalFilename(snippet, snippet.id, snippet.name);
118
150
  return;
119
151
  }
120
152
  this.log(`➕ Creating new snippet: ${snippet.name}`);
121
153
  const response = await got.post(endpoint, { headers, json: payload }).json();
122
154
  const created = adapter.fromRemote(response);
123
- await this.injectIdIntoMeta(snippet.path, created.id);
124
155
  this.log(`✅ Created: ${snippet.name} (id: ${created.id})`);
156
+ await this.ensureCanonicalFilename(snippet, created.id, created.name);
125
157
  }
126
158
  catch (error) {
127
159
  this.error(`❌ Error pushing snippet ${snippet.name}: ${error.message}`);
@@ -1,6 +1,8 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class RemoveEnv extends Command {
2
+ export default class Status extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  run(): Promise<void>;
6
+ private reportActiveProject;
7
+ private reportPinnedProject;
6
8
  }
@@ -0,0 +1,66 @@
1
+ import { Command, ux } from '@oclif/core';
2
+ import { configManager } from '../config/project-config.manager.js';
3
+ import { readLocalConfig } from '../utils/loopress-config.js';
4
+ const c = ux.colorize;
5
+ export default class Status extends Command {
6
+ static description = 'Show which WordPress project and environment commands will target';
7
+ static examples = ['$ lps status'];
8
+ async run() {
9
+ await this.parse(Status);
10
+ const localConfig = await readLocalConfig();
11
+ if (localConfig.projectId) {
12
+ this.reportPinnedProject(localConfig.projectId);
13
+ return;
14
+ }
15
+ this.reportActiveProject();
16
+ }
17
+ reportActiveProject() {
18
+ const env = configManager.getCurrentEnv();
19
+ if (!env) {
20
+ this.log('No project configured. Run `lps project config` first.');
21
+ return;
22
+ }
23
+ const project = configManager.getCurrentProject();
24
+ if (!project) {
25
+ this.log('No project configured. Run `lps project config` first.');
26
+ return;
27
+ }
28
+ this.log(`Project: ${project.name} (${env.name})`);
29
+ this.log(`URL: ${env.url}`);
30
+ }
31
+ reportPinnedProject(projectId) {
32
+ const project = configManager.getProject(projectId);
33
+ if (!project) {
34
+ this.log(`loopress.json pins project "${projectId}", but it no longer exists.`);
35
+ this.log('Run `lps project config` to configure it.');
36
+ return;
37
+ }
38
+ const envNames = Object.keys(project.environments);
39
+ if (envNames.length === 0) {
40
+ this.log(`Project: ${project.name}`);
41
+ this.log('No environments configured for this project. Run `lps project config` to add one.');
42
+ return;
43
+ }
44
+ if (envNames.length === 1) {
45
+ const env = project.environments[envNames[0]];
46
+ this.log(`Project: ${project.name} (${env.name})`);
47
+ this.log(`URL: ${env.url}`);
48
+ return;
49
+ }
50
+ const current = configManager.getCurrentProject();
51
+ const currentEnv = current?.id === projectId ? configManager.getCurrentEnv() : null;
52
+ if (!currentEnv) {
53
+ this.log(`Project: ${project.name} ${c('yellow', '(ambiguous)')}`);
54
+ this.log(`Environments: ${envNames.join(', ')}`);
55
+ this.log('');
56
+ this.warn(`"${project.name}" has multiple environments and isn't the globally active project.`);
57
+ this.log('Run `lps project switch` to pick one before running commands here.');
58
+ if (current) {
59
+ this.log(`(Globally active project right now: "${current.name}")`);
60
+ }
61
+ return;
62
+ }
63
+ this.log(`Project: ${project.name} (${currentEnv.name})`);
64
+ this.log(`URL: ${currentEnv.url}`);
65
+ }
66
+ }
@@ -4,25 +4,32 @@ export declare class ProjectConfigManager {
4
4
  private static instance;
5
5
  constructor(homeDir?: string);
6
6
  static getInstance(): ProjectConfigManager;
7
+ createProjectId(): string;
7
8
  ensureConfigDir(): void;
8
9
  getConfigFilePath(): string;
9
10
  getCurrentEnv(): EnvironmentConfig | null;
10
- getCurrentProject(): null | ProjectConfig;
11
- getEnvironment(projectName: string, envName: string): EnvironmentConfig | null;
12
- getProject(name: string): null | ProjectConfig;
13
- listEnvironments(projectName: string): Array<EnvironmentConfig & {
11
+ getCurrentProject(): null | (ProjectConfig & {
12
+ id: string;
13
+ });
14
+ getEnvironment(projectId: string, envName: string): EnvironmentConfig | null;
15
+ getProject(id: string): null | ProjectConfig;
16
+ listEnvironments(projectId: string): Array<EnvironmentConfig & {
14
17
  isCurrent: boolean;
15
18
  }>;
16
19
  listProjects(): Array<ProjectConfig & {
20
+ id: string;
17
21
  isCurrent: boolean;
18
22
  }>;
19
23
  readConfig(): LoopressConfig;
20
- removeEnvironment(projectName: string, envName: string): void;
21
- removeProject(name: string): void;
22
- setCurrentEnv(projectName: string, envName: string): void;
23
- setCurrentProject(name: string): void;
24
- setEnvironment(projectName: string, envName: string, env: EnvironmentConfig): void;
25
- setProject(name: string, project: ProjectConfig): void;
24
+ removeEnvironment(projectId: string, envName: string): void;
25
+ removeProject(id: string): void;
26
+ setCurrent(projectId: string, envName: string): void;
27
+ setEnvironment(projectId: string, envName: string, env: EnvironmentConfig): void;
28
+ setProject(id: string, project: ProjectConfig): void;
26
29
  writeConfig(config: LoopressConfig): void;
30
+ private isProjectConfig;
31
+ private sanitizeConfig;
32
+ private sanitizeCurrentProject;
33
+ private sanitizeProjects;
27
34
  }
28
35
  export declare const configManager: ProjectConfigManager;