@git.zone/tsdoc 2.0.5 → 2.0.6
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.
- package/.smartconfig.json +21 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/aidocs_classes/commit.d.ts +3 -0
- package/dist_ts/aidocs_classes/commit.js +122 -115
- package/dist_ts/aidocs_classes/description.js +58 -14
- package/dist_ts/aidocs_classes/projectcontext.d.ts +2 -1
- package/dist_ts/aidocs_classes/projectcontext.js +24 -8
- package/dist_ts/aidocs_classes/readme.js +28 -20
- package/dist_ts/classes.aidoc.d.ts +39 -1
- package/dist_ts/classes.aidoc.js +260 -62
- package/dist_ts/classes.diffprocessor.d.ts +1 -0
- package/dist_ts/classes.diffprocessor.js +25 -16
- package/dist_ts/classes.typedoc.js +33 -11
- package/dist_ts/cli.js +69 -11
- package/dist_ts/helpers.agenttools.d.ts +2 -0
- package/dist_ts/helpers.agenttools.js +112 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/plugins.d.ts +5 -2
- package/dist_ts/plugins.js +6 -3
- package/package.json +10 -11
- package/readme.md +35 -14
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/aidocs_classes/commit.ts +134 -134
- package/ts/aidocs_classes/description.ts +60 -17
- package/ts/aidocs_classes/projectcontext.ts +24 -24
- package/ts/aidocs_classes/readme.ts +28 -21
- package/ts/classes.aidoc.ts +315 -63
- package/ts/classes.diffprocessor.ts +23 -13
- package/ts/classes.typedoc.ts +35 -12
- package/ts/cli.ts +72 -10
- package/ts/helpers.agenttools.ts +125 -0
- package/ts/index.ts +1 -0
- package/ts/plugins.ts +6 -1
package/ts/classes.typedoc.ts
CHANGED
|
@@ -9,7 +9,8 @@ export class TypeDoc {
|
|
|
9
9
|
|
|
10
10
|
// Static
|
|
11
11
|
public static async isTypeDocDir(dirPathArg: string): Promise<boolean> {
|
|
12
|
-
return
|
|
12
|
+
return await plugins.fsInstance.file(plugins.path.join(dirPathArg, 'ts/index.ts')).exists()
|
|
13
|
+
|| await plugins.fsInstance.file(plugins.path.join(dirPathArg, 'ts_web/index.ts')).exists();
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Instance
|
|
@@ -32,26 +33,48 @@ export class TypeDoc {
|
|
|
32
33
|
include: [] as string[],
|
|
33
34
|
};
|
|
34
35
|
let startDirectory = '';
|
|
35
|
-
if (await plugins.fsInstance.directory(plugins.path.join(
|
|
36
|
-
data.include.push(plugins.path.join(
|
|
36
|
+
if (await plugins.fsInstance.directory(plugins.path.join(this.typedocDirectory, './ts')).exists()) {
|
|
37
|
+
data.include.push(plugins.path.join(this.typedocDirectory, './ts/**/*'));
|
|
37
38
|
startDirectory = 'ts';
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
if (await plugins.fsInstance.directory(plugins.path.join(
|
|
41
|
-
data.include.push(plugins.path.join(
|
|
41
|
+
if (await plugins.fsInstance.directory(plugins.path.join(this.typedocDirectory, './ts_web')).exists()) {
|
|
42
|
+
data.include.push(plugins.path.join(this.typedocDirectory, './ts_web/**/*'));
|
|
42
43
|
if (!startDirectory) {
|
|
43
44
|
startDirectory = 'ts_web';
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
if (!startDirectory) {
|
|
49
|
+
throw new Error(`No TypeDoc entrypoint found in ${this.typedocDirectory}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tempDir = plugins.path.join(this.typedocDirectory, '.nogit', 'tsdoc');
|
|
53
|
+
const tempTsconfigFile = plugins.path.join(tempDir, 'tsconfig.json');
|
|
54
|
+
await plugins.fsInstance.directory(tempDir).recursive().create();
|
|
55
|
+
await plugins.fsInstance.file(tempTsconfigFile).encoding('utf8').write(JSON.stringify(data));
|
|
56
|
+
const publicDir = plugins.path.join(this.typedocDirectory, 'public');
|
|
57
|
+
let targetDir = publicDir;
|
|
49
58
|
if (options?.publicSubdir) {
|
|
50
|
-
targetDir = plugins.path.join(targetDir, options.publicSubdir);
|
|
59
|
+
targetDir = plugins.path.resolve(plugins.path.join(targetDir, options.publicSubdir));
|
|
60
|
+
const resolvedPublicDir = plugins.path.resolve(publicDir);
|
|
61
|
+
if (!targetDir.startsWith(`${resolvedPublicDir}${plugins.path.sep}`) && targetDir !== resolvedPublicDir) {
|
|
62
|
+
throw new Error(`Invalid publicSubdir outside public directory: ${options.publicSubdir}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const result = await this.smartshellInstance.exec(
|
|
67
|
+
`typedoc --tsconfig ${shellQuote(tempTsconfigFile)} --out ${shellQuote(targetDir)} ${shellQuote(plugins.path.join(this.typedocDirectory, startDirectory, 'index.ts'))}`,
|
|
68
|
+
);
|
|
69
|
+
if (result.exitCode !== 0) {
|
|
70
|
+
throw new Error('typedoc command failed.');
|
|
71
|
+
}
|
|
72
|
+
} finally {
|
|
73
|
+
if (await plugins.fsInstance.file(tempTsconfigFile).exists()) {
|
|
74
|
+
await plugins.fsInstance.file(tempTsconfigFile).delete();
|
|
75
|
+
}
|
|
51
76
|
}
|
|
52
|
-
await this.smartshellInstance.exec(
|
|
53
|
-
`typedoc --tsconfig ${paths.tsconfigFile} --out ${targetDir} ${startDirectory}/index.ts`,
|
|
54
|
-
);
|
|
55
|
-
await plugins.fsInstance.file(paths.tsconfigFile).delete();
|
|
56
77
|
}
|
|
57
78
|
}
|
|
79
|
+
|
|
80
|
+
const shellQuote = (value: string): string => `'${value.replaceAll("'", "'\\''")}'`;
|
package/ts/cli.ts
CHANGED
|
@@ -4,6 +4,54 @@ import { logger } from './logging.js';
|
|
|
4
4
|
|
|
5
5
|
import { TypeDoc } from './classes.typedoc.js';
|
|
6
6
|
import { AiDoc } from './classes.aidoc.js';
|
|
7
|
+
import { NoChangesError } from './aidocs_classes/commit.js';
|
|
8
|
+
|
|
9
|
+
const defaultChatGptAuthSources: plugins.smartaiOpenAiChatGptAuth.TOpenAiChatGptAuthSource[] = [
|
|
10
|
+
'opencode',
|
|
11
|
+
'codex',
|
|
12
|
+
'smartai',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const createAiDoc = async (argvArg: any): Promise<AiDoc> => {
|
|
16
|
+
const aidocInstance = new AiDoc(argvArg);
|
|
17
|
+
await aidocInstance.start();
|
|
18
|
+
return aidocInstance;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getSmartcliArgv = (): string[] | undefined => {
|
|
22
|
+
if (process.argv.length === 0) return undefined;
|
|
23
|
+
if (process.argv[0] === process.execPath) return undefined;
|
|
24
|
+
return [process.execPath, 'tsdoc', ...process.argv];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const handleAuthCommand = async (argvArg: any): Promise<void> => {
|
|
28
|
+
const subcommand = argvArg._?.[1] ?? 'status';
|
|
29
|
+
if (subcommand === 'login') {
|
|
30
|
+
const deviceCode = await plugins.smartai.requestOpenAiChatGptDeviceCode();
|
|
31
|
+
console.log(`Open ${deviceCode.verificationUrl} and enter code ${deviceCode.userCode}`);
|
|
32
|
+
const tokenData = await plugins.smartai.completeOpenAiChatGptDeviceCodeLogin(deviceCode);
|
|
33
|
+
const authFilePath = plugins.smartaiOpenAiChatGptAuth.getDefaultOpenAiChatGptAuthPath('smartai');
|
|
34
|
+
await plugins.smartaiOpenAiChatGptAuth.writeOpenAiChatGptAuthFile(authFilePath, tokenData, 'smartai');
|
|
35
|
+
logger.log('success', `Stored OpenAI ChatGPT auth at ${authFilePath}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const inspections = await plugins.smartaiOpenAiChatGptAuth.inspectOpenAiChatGptAuthSources({
|
|
40
|
+
sources: defaultChatGptAuthSources,
|
|
41
|
+
});
|
|
42
|
+
console.log('OpenAI ChatGPT auth sources:');
|
|
43
|
+
for (const inspection of inspections) {
|
|
44
|
+
const status = inspection.usable
|
|
45
|
+
? 'usable'
|
|
46
|
+
: inspection.exists
|
|
47
|
+
? inspection.expired
|
|
48
|
+
? 'expired'
|
|
49
|
+
: 'not usable'
|
|
50
|
+
: 'missing';
|
|
51
|
+
const account = inspection.email ? ` (${inspection.email})` : '';
|
|
52
|
+
console.log(` ${inspection.source}: ${status}${account} - ${inspection.filePath}`);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
7
55
|
|
|
8
56
|
export const run = async () => {
|
|
9
57
|
const tsdocCli = new plugins.smartcli.Smartcli();
|
|
@@ -28,8 +76,7 @@ export const run = async () => {
|
|
|
28
76
|
});
|
|
29
77
|
|
|
30
78
|
tsdocCli.addCommand('aidoc').subscribe(async (argvArg) => {
|
|
31
|
-
const aidocInstance =
|
|
32
|
-
await aidocInstance.start();
|
|
79
|
+
const aidocInstance = await createAiDoc(argvArg);
|
|
33
80
|
|
|
34
81
|
logger.log('info', `Generating new readme...`);
|
|
35
82
|
logger.log('info', `This may take some time...`);
|
|
@@ -39,9 +86,12 @@ export const run = async () => {
|
|
|
39
86
|
await aidocInstance.buildDescription(paths.cwd);
|
|
40
87
|
});
|
|
41
88
|
|
|
89
|
+
tsdocCli.addCommand('aidocs').subscribe(async (argvArg) => {
|
|
90
|
+
tsdocCli.triggerCommand('aidoc', argvArg);
|
|
91
|
+
});
|
|
92
|
+
|
|
42
93
|
tsdocCli.addCommand('readme').subscribe(async (argvArg) => {
|
|
43
|
-
const aidocInstance =
|
|
44
|
-
await aidocInstance.start();
|
|
94
|
+
const aidocInstance = await createAiDoc(argvArg);
|
|
45
95
|
|
|
46
96
|
logger.log('info', `Generating new readme...`);
|
|
47
97
|
logger.log('info', `This may take some time...`);
|
|
@@ -49,8 +99,7 @@ export const run = async () => {
|
|
|
49
99
|
});
|
|
50
100
|
|
|
51
101
|
tsdocCli.addCommand('description').subscribe(async (argvArg) => {
|
|
52
|
-
const aidocInstance =
|
|
53
|
-
await aidocInstance.start();
|
|
102
|
+
const aidocInstance = await createAiDoc(argvArg);
|
|
54
103
|
|
|
55
104
|
logger.log('info', `Generating new description and keywords...`);
|
|
56
105
|
logger.log('info', `This may take some time...`);
|
|
@@ -58,17 +107,30 @@ export const run = async () => {
|
|
|
58
107
|
});
|
|
59
108
|
|
|
60
109
|
tsdocCli.addCommand('commit').subscribe(async (argvArg) => {
|
|
61
|
-
const aidocInstance =
|
|
62
|
-
await aidocInstance.start();
|
|
110
|
+
const aidocInstance = await createAiDoc(argvArg);
|
|
63
111
|
|
|
64
112
|
logger.log('info', `Generating commit message...`);
|
|
65
113
|
logger.log('info', `This may take some time...`);
|
|
66
|
-
|
|
114
|
+
let commitObject: Awaited<ReturnType<AiDoc['buildNextCommitObject']>>;
|
|
115
|
+
try {
|
|
116
|
+
commitObject = await aidocInstance.buildNextCommitObject(paths.cwd);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error instanceof NoChangesError || (error as Error).name === 'NoChangesError') {
|
|
119
|
+
logger.log('info', 'No uncommitted changes found.');
|
|
120
|
+
console.log(JSON.stringify({ ok: true, noChanges: true }, null, 2));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
67
125
|
|
|
68
126
|
logger.log('ok', `Commit message generated:`);
|
|
69
127
|
console.log(JSON.stringify(commitObject, null, 2));
|
|
70
128
|
});
|
|
71
129
|
|
|
130
|
+
tsdocCli.addCommand('auth').subscribe(async (argvArg) => {
|
|
131
|
+
await handleAuthCommand(argvArg);
|
|
132
|
+
});
|
|
133
|
+
|
|
72
134
|
tsdocCli.addCommand('test').subscribe((argvArg) => {
|
|
73
135
|
tsdocCli.triggerCommand('typedoc', argvArg);
|
|
74
136
|
process.on('exit', async () => {
|
|
@@ -76,5 +138,5 @@ export const run = async () => {
|
|
|
76
138
|
});
|
|
77
139
|
});
|
|
78
140
|
|
|
79
|
-
tsdocCli.startParse();
|
|
141
|
+
tsdocCli.startParse(getSmartcliArgv());
|
|
80
142
|
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
const defaultDeniedSegments = new Set([
|
|
4
|
+
'.cache',
|
|
5
|
+
'.git',
|
|
6
|
+
'.next',
|
|
7
|
+
'.nogit',
|
|
8
|
+
'.rpt2_cache',
|
|
9
|
+
'build',
|
|
10
|
+
'coverage',
|
|
11
|
+
'dist',
|
|
12
|
+
'node_modules',
|
|
13
|
+
'out',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const defaultDeniedBasenames = new Set([
|
|
17
|
+
'.npmrc',
|
|
18
|
+
'bun.lockb',
|
|
19
|
+
'credentials.json',
|
|
20
|
+
'deno.lock',
|
|
21
|
+
'npm-shrinkwrap.json',
|
|
22
|
+
'package-lock.json',
|
|
23
|
+
'pnpm-lock.yaml',
|
|
24
|
+
'yarn.lock',
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const normalizeRelativePath = (rootDir: string, inputPath: string): string => {
|
|
28
|
+
const resolvedRoot = plugins.path.resolve(rootDir);
|
|
29
|
+
const resolvedPath = plugins.path.resolve(
|
|
30
|
+
plugins.path.isAbsolute(inputPath) ? inputPath : plugins.path.join(resolvedRoot, inputPath),
|
|
31
|
+
);
|
|
32
|
+
if (resolvedPath !== resolvedRoot && !resolvedPath.startsWith(`${resolvedRoot}${plugins.path.sep}`)) {
|
|
33
|
+
throw new Error(`Access denied: "${inputPath}" is outside allowed root "${rootDir}"`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const relativePath = plugins.path.relative(resolvedRoot, resolvedPath) || '.';
|
|
37
|
+
assertAllowedRelativePath(relativePath);
|
|
38
|
+
return resolvedPath;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const assertAllowedRelativePath = (relativePath: string): void => {
|
|
42
|
+
const normalized = relativePath.split(plugins.path.sep).join('/');
|
|
43
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
44
|
+
const basename = parts.at(-1) ?? normalized;
|
|
45
|
+
|
|
46
|
+
if (basename === '.env' || basename.startsWith('.env.')) {
|
|
47
|
+
throw new Error(`Access denied: ${relativePath} may contain environment secrets.`);
|
|
48
|
+
}
|
|
49
|
+
if (basename.endsWith('.pem') || basename.endsWith('.key') || basename.endsWith('.p12')) {
|
|
50
|
+
throw new Error(`Access denied: ${relativePath} may contain private key material.`);
|
|
51
|
+
}
|
|
52
|
+
if (defaultDeniedBasenames.has(basename)) {
|
|
53
|
+
throw new Error(`Access denied: ${relativePath} is excluded from AI file access.`);
|
|
54
|
+
}
|
|
55
|
+
for (const segment of parts) {
|
|
56
|
+
if (defaultDeniedSegments.has(segment) || segment.startsWith('dist_')) {
|
|
57
|
+
throw new Error(`Access denied: ${relativePath} is excluded from AI file access.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const listDirectory = async (dirPath: string, recursive = false, prefix = ''): Promise<string[]> => {
|
|
63
|
+
const entries = await plugins.fs.readdir(dirPath, { withFileTypes: true });
|
|
64
|
+
const result: string[] = [];
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const relativeEntryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
67
|
+
try {
|
|
68
|
+
assertAllowedRelativePath(relativeEntryPath);
|
|
69
|
+
} catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
result.push(`${relativeEntryPath}${entry.isDirectory() ? '/' : ''}`);
|
|
73
|
+
if (recursive && entry.isDirectory()) {
|
|
74
|
+
result.push(...await listDirectory(plugins.path.join(dirPath, entry.name), true, relativeEntryPath));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const createReadOnlyFileSystemTools = (rootDirArg: string): plugins.smartai.ToolSet => {
|
|
81
|
+
const rootDir = plugins.path.resolve(rootDirArg);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
read_file: plugins.smartagent.tool({
|
|
85
|
+
description: 'Read file contents within the project. Secret and generated paths are blocked.',
|
|
86
|
+
inputSchema: plugins.smartagent.z.object({
|
|
87
|
+
path: plugins.smartagent.z.string().describe('Absolute or project-relative path to the file'),
|
|
88
|
+
startLine: plugins.smartagent.z.number().optional().describe('First line, 1-indexed and inclusive'),
|
|
89
|
+
endLine: plugins.smartagent.z.number().optional().describe('Last line, 1-indexed and inclusive'),
|
|
90
|
+
}),
|
|
91
|
+
execute: async ({ path: filePath, startLine, endLine }: {
|
|
92
|
+
path: string;
|
|
93
|
+
startLine?: number;
|
|
94
|
+
endLine?: number;
|
|
95
|
+
}) => {
|
|
96
|
+
const resolvedPath = normalizeRelativePath(rootDir, filePath);
|
|
97
|
+
const stat = await plugins.fs.stat(resolvedPath);
|
|
98
|
+
if (!stat.isFile()) {
|
|
99
|
+
throw new Error(`Cannot read non-file path: ${filePath}`);
|
|
100
|
+
}
|
|
101
|
+
const content = await plugins.fs.readFile(resolvedPath, 'utf8');
|
|
102
|
+
const selectedContent = startLine !== undefined || endLine !== undefined
|
|
103
|
+
? content.split('\n').slice((startLine ?? 1) - 1, endLine).join('\n')
|
|
104
|
+
: content;
|
|
105
|
+
return plugins.smartagent.truncateOutput(selectedContent).content;
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
list_directory: plugins.smartagent.tool({
|
|
109
|
+
description: 'List project files and directories. Secret and generated paths are omitted.',
|
|
110
|
+
inputSchema: plugins.smartagent.z.object({
|
|
111
|
+
path: plugins.smartagent.z.string().describe('Absolute or project-relative directory path to list'),
|
|
112
|
+
recursive: plugins.smartagent.z.boolean().optional().describe('List recursively'),
|
|
113
|
+
}),
|
|
114
|
+
execute: async ({ path: dirPath, recursive }: { path: string; recursive?: boolean }) => {
|
|
115
|
+
const resolvedPath = normalizeRelativePath(rootDir, dirPath);
|
|
116
|
+
const stat = await plugins.fs.stat(resolvedPath);
|
|
117
|
+
if (!stat.isDirectory()) {
|
|
118
|
+
throw new Error(`Cannot list non-directory path: ${dirPath}`);
|
|
119
|
+
}
|
|
120
|
+
const entries = await listDirectory(resolvedPath, recursive === true);
|
|
121
|
+
return plugins.smartagent.truncateOutput(entries.join('\n')).content;
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
};
|
package/ts/index.ts
CHANGED
package/ts/plugins.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// node native
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
2
3
|
import * as path from 'path';
|
|
3
4
|
|
|
4
|
-
export { path };
|
|
5
|
+
export { fs, path };
|
|
5
6
|
|
|
6
7
|
// pushrocks scope
|
|
7
8
|
import * as smartconfig from '@push.rocks/smartconfig';
|
|
8
9
|
import * as qenv from '@push.rocks/qenv';
|
|
9
10
|
import * as smartagent from '@push.rocks/smartagent';
|
|
11
|
+
import * as smartagentCompaction from '@push.rocks/smartagent/compaction';
|
|
10
12
|
import * as smartagentTools from '@push.rocks/smartagent/tools';
|
|
11
13
|
import * as smartai from '@push.rocks/smartai';
|
|
14
|
+
import * as smartaiOpenAiChatGptAuth from '@push.rocks/smartai/openai-chatgpt-auth';
|
|
12
15
|
import * as smartcli from '@push.rocks/smartcli';
|
|
13
16
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
14
17
|
import * as smartfile from '@push.rocks/smartfile';
|
|
@@ -25,8 +28,10 @@ export {
|
|
|
25
28
|
smartconfig,
|
|
26
29
|
qenv,
|
|
27
30
|
smartagent,
|
|
31
|
+
smartagentCompaction,
|
|
28
32
|
smartagentTools,
|
|
29
33
|
smartai,
|
|
34
|
+
smartaiOpenAiChatGptAuth,
|
|
30
35
|
smartcli,
|
|
31
36
|
smartdelay,
|
|
32
37
|
smartfile,
|