ai-summon 0.0.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 (61) hide show
  1. package/.claude/commands/speckit.analyze.md +184 -0
  2. package/.claude/commands/speckit.checklist.md +294 -0
  3. package/.claude/commands/speckit.clarify.md +177 -0
  4. package/.claude/commands/speckit.constitution.md +78 -0
  5. package/.claude/commands/speckit.implement.md +121 -0
  6. package/.claude/commands/speckit.plan.md +81 -0
  7. package/.claude/commands/speckit.specify.md +204 -0
  8. package/.claude/commands/speckit.tasks.md +108 -0
  9. package/.claude/settings.local.json +23 -0
  10. package/.prettierignore +5 -0
  11. package/.prettierrc.json +10 -0
  12. package/.specify/memory/constitution.md +72 -0
  13. package/.specify/scripts/bash/check-prerequisites.sh +166 -0
  14. package/.specify/scripts/bash/common.sh +113 -0
  15. package/.specify/scripts/bash/create-new-feature.sh +97 -0
  16. package/.specify/scripts/bash/setup-plan.sh +60 -0
  17. package/.specify/scripts/bash/update-agent-context.sh +738 -0
  18. package/.specify/templates/agent-file-template.md +28 -0
  19. package/.specify/templates/checklist-template.md +40 -0
  20. package/.specify/templates/plan-template.md +111 -0
  21. package/.specify/templates/spec-template.md +115 -0
  22. package/.specify/templates/tasks-template.md +250 -0
  23. package/CLAUDE.md +199 -0
  24. package/PRD.md +268 -0
  25. package/README.md +171 -0
  26. package/dist/ai-summon.d.ts +2 -0
  27. package/dist/ai-summon.js +73 -0
  28. package/dist/commands/ide/index.d.ts +3 -0
  29. package/dist/commands/ide/index.js +253 -0
  30. package/dist/commands/init.d.ts +4 -0
  31. package/dist/commands/init.js +55 -0
  32. package/dist/commands/url.d.ts +4 -0
  33. package/dist/commands/url.js +223 -0
  34. package/dist/types/index.d.ts +40 -0
  35. package/dist/types/index.js +1 -0
  36. package/dist/util.d.ts +16 -0
  37. package/dist/util.js +109 -0
  38. package/eslint.config.js +47 -0
  39. package/package.json +47 -0
  40. package/specs/001-cloud-login-feature/contracts/cloud-command.ts +82 -0
  41. package/specs/001-cloud-login-feature/contracts/config-service.ts +170 -0
  42. package/specs/001-cloud-login-feature/data-model.md +269 -0
  43. package/specs/001-cloud-login-feature/plan.md +91 -0
  44. package/specs/001-cloud-login-feature/quickstart.md +366 -0
  45. package/specs/001-cloud-login-feature/research.md +290 -0
  46. package/specs/001-cloud-login-feature/spec.md +195 -0
  47. package/specs/001-cloud-login-feature/tasks.md +235 -0
  48. package/specs/001-cloud-scp-command/contracts/cloud-scp-api.ts +402 -0
  49. package/specs/001-cloud-scp-command/data-model.md +424 -0
  50. package/specs/001-cloud-scp-command/plan.md +124 -0
  51. package/specs/001-cloud-scp-command/quickstart.md +536 -0
  52. package/specs/001-cloud-scp-command/research.md +345 -0
  53. package/specs/001-cloud-scp-command/spec.md +248 -0
  54. package/specs/001-cloud-scp-command/tasks.md +434 -0
  55. package/src/ai-summon.ts +88 -0
  56. package/src/commands/ide/index.ts +322 -0
  57. package/src/commands/init.ts +64 -0
  58. package/src/commands/url.ts +262 -0
  59. package/src/types/index.ts +49 -0
  60. package/src/util.ts +146 -0
  61. package/tsconfig.json +21 -0
package/dist/util.js ADDED
@@ -0,0 +1,109 @@
1
+ import { readFileSync, existsSync, readdirSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { join, basename } from 'path';
3
+ import { dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { homedir } from 'os';
6
+ import chalk from 'chalk';
7
+ export const getPackageJson = () => {
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
11
+ return packageJson;
12
+ };
13
+ export const getConfigPath = () => {
14
+ const dir = join(homedir(), '.ai');
15
+ mkdirSync(dir, { recursive: true });
16
+ return join(dir, 'config.json');
17
+ };
18
+ export const readConfig = () => {
19
+ const configPath = getConfigPath();
20
+ if (!existsSync(configPath)) {
21
+ console.log(chalk.yellow(`Configuration file not found: ${configPath}\n` +
22
+ `Run "ai init" to create it and set your workingDirectory.`));
23
+ process.exit(1);
24
+ }
25
+ const configContent = readFileSync(configPath, 'utf-8');
26
+ const config = JSON.parse(configContent);
27
+ // If workingDirectory is configured but repos/yiren are not, initialize them
28
+ if (config.workingDirectory && !config.repos) {
29
+ return {
30
+ workingDirectory: config.workingDirectory,
31
+ repos: {},
32
+ yiren: config.yiren || {},
33
+ urls: config.urls,
34
+ urlGroups: config.urlGroups,
35
+ };
36
+ }
37
+ return config;
38
+ };
39
+ const ensureAiDir = () => {
40
+ const dir = join(homedir(), '.ai');
41
+ mkdirSync(dir, { recursive: true });
42
+ return dir;
43
+ };
44
+ const getIdeReposCachePath = () => {
45
+ const dir = ensureAiDir();
46
+ return join(dir, 'ide-repos-cache.json');
47
+ };
48
+ export const readIdeReposCache = (workingDirectory) => {
49
+ const cachePath = getIdeReposCachePath();
50
+ if (!existsSync(cachePath))
51
+ return null;
52
+ try {
53
+ const raw = readFileSync(cachePath, 'utf-8');
54
+ const parsed = JSON.parse(raw);
55
+ if (parsed.version !== 1)
56
+ return null;
57
+ if (parsed.workingDirectory !== workingDirectory)
58
+ return null;
59
+ if (!Array.isArray(parsed.repos))
60
+ return null;
61
+ return parsed.repos;
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ };
67
+ export const writeIdeReposCache = (workingDirectory, repos) => {
68
+ const cachePath = getIdeReposCachePath();
69
+ const payload = {
70
+ version: 1,
71
+ workingDirectory,
72
+ updatedAt: Date.now(),
73
+ repos,
74
+ };
75
+ writeFileSync(cachePath, JSON.stringify(payload, null, 2), 'utf-8');
76
+ };
77
+ export const findGitRepositories = (workingDirectory) => {
78
+ const repositories = [];
79
+ const scanDirectory = (dir, topLevelFolder = '/') => {
80
+ try {
81
+ // Check if current directory contains .git folder
82
+ if (existsSync(join(dir, '.git'))) {
83
+ repositories.push({
84
+ name: basename(dir),
85
+ path: dir,
86
+ topLevelFolder,
87
+ });
88
+ return; // Stop recursing into this directory
89
+ }
90
+ // Continue scanning subdirectories
91
+ const entries = readdirSync(dir, { withFileTypes: true });
92
+ for (const entry of entries) {
93
+ if (entry.isDirectory()) {
94
+ const fullPath = join(dir, entry.name);
95
+ // Determine top-level folder for this entry
96
+ const isTopLevel = dir === workingDirectory;
97
+ const currentTopLevel = isTopLevel ? entry.name : topLevelFolder;
98
+ scanDirectory(fullPath, currentTopLevel);
99
+ }
100
+ }
101
+ }
102
+ catch {
103
+ // Silently skip directories with permission errors
104
+ // This will be enhanced in future iterations
105
+ }
106
+ };
107
+ scanDirectory(workingDirectory);
108
+ return repositories;
109
+ };
@@ -0,0 +1,47 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from '@typescript-eslint/eslint-plugin';
3
+ import tsparser from '@typescript-eslint/parser';
4
+ import prettier from 'eslint-plugin-prettier';
5
+ import prettierConfig from 'eslint-config-prettier';
6
+ import globals from 'globals';
7
+
8
+ export default [
9
+ {
10
+ ignores: ['dist/**', 'node_modules/**', '**/*.js'],
11
+ },
12
+ eslint.configs.recommended,
13
+ {
14
+ files: ['src/**/*.ts'],
15
+ languageOptions: {
16
+ parser: tsparser,
17
+ parserOptions: {
18
+ ecmaVersion: 2020,
19
+ sourceType: 'module',
20
+ project: './tsconfig.json',
21
+ },
22
+ globals: {
23
+ ...globals.node,
24
+ $: 'readonly',
25
+ },
26
+ },
27
+ plugins: {
28
+ '@typescript-eslint': tseslint,
29
+ prettier: prettier,
30
+ },
31
+ rules: {
32
+ ...tseslint.configs.recommended.rules,
33
+ ...prettierConfig.rules,
34
+ 'prettier/prettier': 'error',
35
+ '@typescript-eslint/explicit-function-return-type': 'off',
36
+ '@typescript-eslint/no-explicit-any': 'warn',
37
+ '@typescript-eslint/no-unused-vars': [
38
+ 'error',
39
+ {
40
+ argsIgnorePattern: '^_',
41
+ varsIgnorePattern: '^_',
42
+ },
43
+ ],
44
+ 'no-console': 'off',
45
+ },
46
+ },
47
+ ];
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "ai-summon",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "build": "rm -rf dist && tsc",
10
+ "build:install": "npm run build && npm uninstall -g && npm install -g",
11
+ "start": "node dist/bin/ai-summon.js",
12
+ "dev": "ts-node bin/ai-summon.ts",
13
+ "lint": "eslint src --ext .ts",
14
+ "lint:fix": "eslint src --ext .ts --fix",
15
+ "format": "prettier --write \"src/**/*.ts\"",
16
+ "format:check": "prettier --check \"src/**/*.ts\""
17
+ },
18
+ "bin": {
19
+ "ai": "./dist/ai-summon.js"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "dependencies": {
25
+ "chalk": "^5.3.0",
26
+ "commander": "^12.0.0",
27
+ "inquirer": "^9.2.15",
28
+ "inquirer-autocomplete-prompt": "^3.0.1",
29
+ "log-symbols": "^6.0.0",
30
+ "ora": "^8.0.1",
31
+ "zx": "^7.2.3"
32
+ },
33
+ "devDependencies": {
34
+ "@types/inquirer": "^9.0.7",
35
+ "@types/inquirer-autocomplete-prompt": "^3.0.3",
36
+ "@types/node": "^20.0.0",
37
+ "@typescript-eslint/eslint-plugin": "^8.48.1",
38
+ "@typescript-eslint/parser": "^8.48.1",
39
+ "eslint": "^9.39.1",
40
+ "eslint-config-prettier": "^10.1.8",
41
+ "eslint-plugin-prettier": "^5.5.4",
42
+ "globals": "^16.5.0",
43
+ "prettier": "^3.7.4",
44
+ "ts-node": "^10.9.0",
45
+ "typescript": "^5.0.0"
46
+ }
47
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Cloud Command Interface Contract
3
+ * Defines the CLI command interface for cloud infrastructure access
4
+ */
5
+
6
+ export interface CloudLoginOptions {
7
+ /** Environment to connect to: dev, staging, or prod */
8
+ env?: 'dev' | 'staging' | 'prod';
9
+
10
+ /** Service name to connect to (e.g., todo-mini, wuhan-mall) */
11
+ service?: string;
12
+ }
13
+
14
+ export interface CloudLoginCommand {
15
+ /**
16
+ * Execute cloud login SSH connection
17
+ * @param options Command options from CLI
18
+ * @returns Promise that resolves when SSH session is established or rejects on error
19
+ */
20
+ execute(options: CloudLoginOptions): Promise<void>;
21
+ }
22
+
23
+ /**
24
+ * Command signature for Commander.js integration
25
+ * Main command: hsh cloud (with subcommands)
26
+ * Usage: hsh cloud login --env dev --service todo-mini
27
+ */
28
+ export interface CloudCommandDefinition {
29
+ name: 'cloud';
30
+ description: 'Cloud infrastructure management commands';
31
+ subcommands: {
32
+ login: {
33
+ description: 'SSH into cloud instances based on environment and service';
34
+ options: {
35
+ '--env <environment>': 'Environment: dev, staging, or prod';
36
+ '--service <service>': 'Service name (e.g., todo-mini, wuhan-mall)';
37
+ };
38
+ examples: [
39
+ 'hsh cloud login --env dev --service todo-mini',
40
+ 'hsh cloud login --env prod --service wuhan-mall',
41
+ 'hsh cloud login # Interactive mode with prompts',
42
+ ];
43
+ };
44
+ // Future subcommands can be added here
45
+ // scp: { ... };
46
+ // status: { ... };
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Error types that can be thrown during cloud login execution
52
+ */
53
+ export enum CloudLoginError {
54
+ CONFIG_NOT_FOUND = 'CONFIG_NOT_FOUND',
55
+ INVALID_CONFIG = 'INVALID_CONFIG',
56
+ SERVICE_NOT_FOUND = 'SERVICE_NOT_FOUND',
57
+ ENVIRONMENT_NOT_FOUND = 'ENVIRONMENT_NOT_FOUND',
58
+ PRIVATE_KEY_NOT_FOUND = 'PRIVATE_KEY_NOT_FOUND',
59
+ PRIVATE_KEY_PERMISSIONS = 'PRIVATE_KEY_PERMISSIONS',
60
+ SSH_CONNECTION_FAILED = 'SSH_CONNECTION_FAILED',
61
+ NETWORK_UNREACHABLE = 'NETWORK_UNREACHABLE',
62
+ AUTHENTICATION_FAILED = 'AUTHENTICATION_FAILED',
63
+ }
64
+
65
+ /**
66
+ * Structured error information for user feedback
67
+ */
68
+ export interface CloudLoginErrorInfo {
69
+ type: CloudLoginError;
70
+ message: string;
71
+ suggestion?: string;
72
+ command?: string;
73
+ }
74
+
75
+ /**
76
+ * Configuration validation result
77
+ */
78
+ export interface ConfigValidationResult {
79
+ valid: boolean;
80
+ errors: CloudLoginErrorInfo[];
81
+ warnings: string[];
82
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Configuration Service Interface Contract
3
+ * Defines the interface for reading and managing hsh configuration
4
+ */
5
+
6
+ import { HshConfig, CloudConfig, Environment } from '../data-model';
7
+
8
+ export interface ConfigService {
9
+ /**
10
+ * Read configuration from ~/.ai/config.json
11
+ * @returns Promise resolving to parsed configuration
12
+ * @throws Error if file not found or invalid JSON
13
+ */
14
+ readConfig(): Promise<HshConfig>;
15
+
16
+ /**
17
+ * Get cloud configuration for specific service and environment
18
+ * @param service Service name (e.g., todo-mini)
19
+ * @param environment Environment name (dev, staging, prod)
20
+ * @returns Cloud configuration or null if not found
21
+ */
22
+ getCloudConfig(service: string, environment: Environment): Promise<CloudConfig | null>;
23
+
24
+ /**
25
+ * Get all available services from configuration
26
+ * @returns Array of service names
27
+ */
28
+ getAvailableServices(): Promise<string[]>;
29
+
30
+ /**
31
+ * Get all available environments for a specific service
32
+ * @param service Service name
33
+ * @returns Array of environment names
34
+ */
35
+ getAvailableEnvironments(service: string): Promise<Environment[]>;
36
+
37
+ /**
38
+ * Validate configuration structure and content
39
+ * @returns Validation result with errors and warnings
40
+ */
41
+ validateConfig(): Promise<ConfigValidationResult>;
42
+
43
+ /**
44
+ * Migrate legacy configuration to new structure
45
+ * @param legacyConfig Legacy configuration object
46
+ * @returns Migrated configuration
47
+ */
48
+ migrateConfig(legacyConfig: any): HshConfig;
49
+
50
+ /**
51
+ * Check if configuration uses legacy format
52
+ * @returns True if configuration needs migration
53
+ */
54
+ isLegacyConfig(): Promise<boolean>;
55
+ }
56
+
57
+ /**
58
+ * SSH Service Interface Contract
59
+ * Defines the interface for SSH connection management
60
+ */
61
+ export interface SSHService {
62
+ /**
63
+ * Execute SSH connection to specified host
64
+ * @param ip Target IP address
65
+ * @param privateKeyFile Path to SSH private key
66
+ * @param username SSH username (default: root)
67
+ * @returns Promise that resolves when connection is established
68
+ */
69
+ connect(ip: string, privateKeyFile: string, username?: string): Promise<void>;
70
+
71
+ /**
72
+ * Validate SSH private key file
73
+ * @param privateKeyFile Path to private key file
74
+ * @returns Promise resolving to validation result
75
+ */
76
+ validatePrivateKey(privateKeyFile: string): Promise<{
77
+ exists: boolean;
78
+ permissions: string;
79
+ isValid: boolean;
80
+ suggestions: string[];
81
+ }>;
82
+
83
+ /**
84
+ * Test SSH connectivity without establishing full session
85
+ * @param ip Target IP address
86
+ * @param privateKeyFile Path to SSH private key
87
+ * @returns Promise resolving to connectivity test result
88
+ */
89
+ testConnectivity(
90
+ ip: string,
91
+ privateKeyFile: string
92
+ ): Promise<{
93
+ reachable: boolean;
94
+ authenticated: boolean;
95
+ latency: number;
96
+ error?: string;
97
+ }>;
98
+ }
99
+
100
+ /**
101
+ * Prompt Service Interface Contract
102
+ * Defines the interface for interactive user prompts
103
+ */
104
+ export interface PromptService {
105
+ /**
106
+ * Prompt user to select service when not provided
107
+ * @param availableServices List of available service names
108
+ * @returns Selected service name
109
+ */
110
+ promptForService(availableServices: string[]): Promise<string>;
111
+
112
+ /**
113
+ * Prompt user to select environment when not provided
114
+ * @param availableEnvironments List of available environments
115
+ * @returns Selected environment
116
+ */
117
+ promptForEnvironment(availableEnvironments: Environment[]): Promise<Environment>;
118
+
119
+ /**
120
+ * Prompt user for confirmation before connecting to production
121
+ * @param service Service name
122
+ * @returns True if user confirms
123
+ */
124
+ confirmProductionAccess(service: string): Promise<boolean>;
125
+
126
+ /**
127
+ * Prompt user for missing configuration values
128
+ * @param missingFields List of missing configuration fields
129
+ * @returns User-provided configuration values
130
+ */
131
+ promptForConfiguration(missingFields: string[]): Promise<Record<string, string>>;
132
+ }
133
+
134
+ /**
135
+ * Migration Service Interface Contract
136
+ * Defines the interface for configuration migration
137
+ */
138
+ export interface MigrationService {
139
+ /**
140
+ * Detect if current configuration needs migration
141
+ * @returns Migration assessment
142
+ */
143
+ assessMigration(): Promise<{
144
+ needsMigration: boolean;
145
+ currentVersion: string;
146
+ targetVersion: string;
147
+ changes: string[];
148
+ }>;
149
+
150
+ /**
151
+ * Execute configuration migration
152
+ * @param backupOriginal Whether to backup original config
153
+ * @returns Migration result
154
+ */
155
+ executeMigration(backupOriginal?: boolean): Promise<{
156
+ success: boolean;
157
+ backupPath?: string;
158
+ errors: string[];
159
+ }>;
160
+
161
+ /**
162
+ * Rollback migration to previous configuration
163
+ * @param backupPath Path to backup configuration
164
+ * @returns Rollback result
165
+ */
166
+ rollbackMigration(backupPath: string): Promise<{
167
+ success: boolean;
168
+ errors: string[];
169
+ }>;
170
+ }