Compare commits

...

123 Commits

Author SHA1 Message Date
1a517fdd1b 1.11.3 2025-12-15 15:54:45 +00:00
90af6eb1b1 update 2025-12-15 15:54:25 +00:00
3485392979 1.11.2 2025-12-15 15:22:45 +00:00
89adae2cff update 2025-12-15 15:22:35 +00:00
3451ab7456 update 2025-12-15 15:14:16 +00:00
bcded1eafa update 2025-12-15 14:34:02 +00:00
9cae46e2fe update 2025-12-15 14:33:58 +00:00
65c1df30da 1.11.1 2025-12-15 12:02:16 +00:00
e8f2add812 fix(dependencies): update 2025-12-15 12:02:13 +00:00
8fcc304ee3 v1.11.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-15 11:36:03 +00:00
69802b46b6 feat(commit): Integrate DualAgentOrchestrator for commit message generation and improve diff/context handling 2025-12-15 11:36:03 +00:00
e500455557 1.10.2
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-13 22:50:26 +00:00
4029691ccd 1.10.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-13 11:42:43 +00:00
3b1c84d7e8 fix(npmextra): update to new format 2025-12-13 11:42:39 +00:00
f8d0895aab v1.10.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-02 12:17:10 +00:00
d7ec2220a1 feat(diff-processor): Improve diff sampling and file prioritization: increase inclusion thresholds, expand sampled context, and boost priority for interface/type and entry-point files 2025-12-02 12:17:10 +00:00
c24ce31b1f 1.9.2
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-04 03:43:27 +00:00
fec2017cc6 fix(deps): Update dependencies and devDependencies to newer versions (bump multiple packages) 2025-11-04 03:43:27 +00:00
88fac91c79 1.9.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-04 02:28:55 +00:00
ce4da89da9 fix(iterative-context-builder): Rely on DiffProcessor for git diff pre-processing; remove raw char truncation, raise diff token safety, and improve logging 2025-11-04 02:28:55 +00:00
6524adea18 1.9.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-04 02:19:57 +00:00
4bf0c02618 feat(context): Add intelligent DiffProcessor to summarize and prioritize git diffs and integrate it into the commit context pipeline 2025-11-04 02:19:57 +00:00
f84a65217d 1.8.3
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-04 01:37:15 +00:00
3f22fc91ae fix(context): Prevent enormous git diffs and OOM during context building by adding exclusion patterns, truncation, and diagnostic logging 2025-11-04 01:37:15 +00:00
11e65b92ec 1.8.2
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-03 17:53:03 +00:00
0a3080518f 1.8.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-03 17:50:09 +00:00
d0a4ddbb4b fix(git diff): improve git diff 2025-11-03 17:49:35 +00:00
481339d3cb 1.8.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-03 13:37:16 +00:00
ebc3d760af feat(context): Wire OpenAI provider through task context factory and add git-diff support to iterative context builder 2025-11-03 13:37:16 +00:00
a6d678e36c 1.7.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-03 13:19:29 +00:00
8c3e16a4f2 feat(IterativeContextBuilder): Add iterative AI-driven context builder and integrate into task factory; add tests and iterative configuration 2025-11-03 13:19:29 +00:00
2276fb0c0c 1.6.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-03 11:04:21 +00:00
0a9d535df4 fix(context): Improve context building, caching and test robustness 2025-11-03 11:04:21 +00:00
d46fd1590e 1.6.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-02 23:07:59 +00:00
1d7317f063 feat(context): Introduce smart context system: analyzer, lazy loader, cache and README/docs improvements 2025-11-02 23:07:59 +00:00
fe5121ec9c 1.5.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-09-07 07:54:04 +00:00
c084b20390 fix(package): Bump dependencies, refine test script and imports, and overhaul README and docs 2025-09-07 07:54:04 +00:00
6f024536a8 1.5.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-16 11:20:39 +00:00
2405fb3370 fix(aidoc): Bump dependencies, add pnpm workspace config, and add AiDoc.stop() 2025-08-16 11:20:39 +00:00
8561940b8c 1.5.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-14 11:27:38 +00:00
ab273ea75c feat(docs): Update project metadata and documentation to reflect comprehensive AI-enhanced features and improved installation and usage instructions 2025-05-14 11:27:38 +00:00
620737566f 1.4.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-13 21:20:10 +00:00
23453bf16b fix(dependencies): Upgrade various dependency versions and update package manager configuration 2025-05-13 21:20:10 +00:00
84947cfb80 1.4.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-02-25 18:33:51 +00:00
1a9ac9091d fix(dependencies): Update dependencies to latest versions 2025-02-25 18:33:51 +00:00
88b93b8b83 1.4.3
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-14 17:42:19 +01:00
77279a9135 fix(aidocs_classes): Improve readme generation instructions to ensure correct markdown formatting. 2025-01-14 17:42:19 +01:00
7426addbdd 1.4.2 2024-10-28 21:45:28 +01:00
58d060d729 fix(cli): Ensure async completion for aidoc readme and description generation 2024-10-28 21:45:28 +01:00
370cbfe6f3 1.4.1 2024-10-28 21:20:51 +01:00
2adb4e8cb0 fix(readme): Correct async call to getModuleSubDirs in readme generation. 2024-10-28 21:20:50 +01:00
e8608b1cae 1.4.0 2024-10-28 21:16:00 +01:00
33fa7fa337 feat(aidocs): Added support for building readmes for sub-modules in aidocs 2024-10-28 21:15:59 +01:00
2946bcaf49 1.3.12 2024-06-24 00:01:37 +02:00
d962e17c18 fix(aidocs): Fix changelog generation by handling leading newlines 2024-06-24 00:01:36 +02:00
a22c400355 1.3.11 2024-06-23 23:59:44 +02:00
08b7305ef0 fix(core): Fixed new changelog formatting issue to retain consistent spacing. 2024-06-23 23:59:43 +02:00
d7b462fda9 1.3.10 2024-06-23 23:58:32 +02:00
01e6c15626 fix(aidocs_classes): Fix changelog format to remove extra newline 2024-06-23 23:58:31 +02:00
94a066247f 1.3.9 2024-06-23 23:50:51 +02:00
7de157ccb3 fix(aidoc): Fix changelog generation by properly stripping markdown code fences 2024-06-23 23:50:51 +02:00
d783965b25 1.3.8 2024-06-23 23:29:37 +02:00
07f1413d5e fix(changelog): Fix changelog generation by properly stripping markdown code fences 2024-06-23 23:29:36 +02:00
d7bf45f6b5 1.3.7 2024-06-23 23:24:09 +02:00
3eb64bcb5d fix(aidoc): Update to include package-lock.json in uncommitted changes check 2024-06-23 23:24:09 +02:00
e24a027fdd 1.3.6 2024-06-23 23:20:02 +02:00
3f451cfcb1 fix(commit): Fixed issue with retrieving uncommitted diffs in git repository 2024-06-23 23:20:01 +02:00
e355c51c8d 1.3.5 2024-06-23 23:05:47 +02:00
b0fcaba2c3 fix(aidocs_classes): Refactor and enhance changelog formatting 2024-06-23 23:05:47 +02:00
4ea205e11b 1.3.4 2024-06-23 22:48:06 +02:00
f819e7b521 fix(aidocs_classes): Fix changelog formatting issue in commit class 2024-06-23 22:48:05 +02:00
d4903f32f0 1.3.3 2024-06-23 22:47:28 +02:00
34102a2544 fix(aidocs_classes): Fix minor bugs and update dependencies in aidocs_classes 2024-06-23 22:47:27 +02:00
5e2171dbfd 1.3.2 2024-06-23 19:55:40 +02:00
70d4af653a fix(aidocs_classes): Fix typo in INextCommitObject interface and update date format in changelog generation. 2024-06-23 19:55:39 +02:00
06f6fdef98 1.3.1 2024-06-23 19:47:44 +02:00
b6fb7bf029 fix(aidocs_classes): Fix typo in INextCommitObject interface 2024-06-23 19:47:43 +02:00
4c83725120 1.3.0 2024-06-23 18:38:35 +02:00
a060cd1a03 feat(core): Added smarttime dependency and improved changelog generation 2024-06-23 18:38:34 +02:00
e8372effc7 1.2.4 2024-06-23 17:37:20 +02:00
571249705e fix(logging): Refactor logger initialization to use commitinfo data 2024-06-23 17:37:19 +02:00
927cd961fd 1.2.3 2024-06-23 17:36:03 +02:00
63b4fcc232 fix(aidocs): Fix bug in AiDoc class causing undefined token handling 2024-06-23 17:36:02 +02:00
4188ed7f24 1.2.2 2024-06-23 16:46:59 +02:00
1feddc6e85 fix(aidocs): Fix bug in AiDoc class causing undefined token handling 2024-06-23 16:46:58 +02:00
499baebc18 1.2.1 2024-06-23 16:46:11 +02:00
01fc0d0c6e fix(core): Fixed usage of plugins in project context and readme generation 2024-06-23 16:46:10 +02:00
b6c9cea5d1 1.2.0 2024-06-23 16:45:21 +02:00
a949039192 feat(aidocs_classes): Enhance changelog generation by supporting complete generation in the absence of previous changelog files 2024-06-23 16:45:20 +02:00
11bde9d756 1.1.42 2024-06-23 16:43:24 +02:00
eac26521c6 fix(aidoc_classes): Improve commit message generation by handling empty diffs and updating changelog instructions 2024-06-23 16:43:23 +02:00
e1323569f5 1.1.41 2024-06-23 13:49:15 +02:00
41e4bd6689 fix(aidoc_classes): Improve commit message generation by handling empty diffs and updating changelog instructions 2024-06-23 13:49:14 +02:00
164a58ec59 1.1.40 2024-06-23 13:04:48 +02:00
e1c0f82fe8 fix(core): update 2024-06-23 13:04:47 +02:00
8a0046818b 1.1.39 2024-06-23 12:38:58 +02:00
97fa9db32f fix(core): update 2024-06-23 12:38:58 +02:00
d61de9b615 1.1.38 2024-06-23 12:27:27 +02:00
fba54035ea fix(core): update 2024-06-23 12:27:26 +02:00
9a3d8588a8 1.1.37 2024-06-23 12:20:07 +02:00
eb8f8fa70a fix(core): update 2024-06-23 12:20:06 +02:00
afe7b5e99e 1.1.36 2024-06-23 12:11:07 +02:00
e074562362 fix(core): update 2024-06-23 12:11:06 +02:00
240d6bb314 1.1.35 2024-06-23 12:03:26 +02:00
2d0839a1da fix(core): update 2024-06-23 12:03:25 +02:00
9f250ae2b3 1.1.34 2024-06-23 11:59:39 +02:00
1223bb8567 fix(core): update 2024-06-23 11:59:38 +02:00
9395cfc166 1.1.33 2024-06-22 21:21:53 +02:00
3b4c6bd97f fix(core): update 2024-06-22 21:21:52 +02:00
5d2c9e6158 1.1.32 2024-06-22 19:13:58 +02:00
89977038ec fix(core): update 2024-06-22 19:13:57 +02:00
b753c206b0 1.1.31 2024-06-22 13:20:56 +02:00
1965bd9b47 fix(core): update 2024-06-22 13:20:55 +02:00
138d71e8c5 1.1.30 2024-06-22 13:11:23 +02:00
15397e8609 fix(core): update 2024-06-22 13:11:22 +02:00
1489420e47 1.1.29 2024-05-17 17:41:50 +02:00
5e3b122b59 fix(core): update 2024-05-17 17:41:49 +02:00
02fa9215d3 1.1.28 2024-05-17 17:38:35 +02:00
32f12c67cf fix(core): update 2024-05-17 17:38:35 +02:00
be53225bb1 1.1.27 2024-04-20 23:14:14 +02:00
a5db530879 fix(core): update 2024-04-20 23:14:13 +02:00
c5b07c2504 1.1.26 2024-04-14 02:23:56 +02:00
1bd215d18d fix(core): update 2024-04-14 02:23:56 +02:00
25 changed files with 13023 additions and 4083 deletions

View File

@@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @ship.zone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@@ -0,0 +1,124 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @git.zone/tsdoc
npmci command tsdoc
continue-on-error: true

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ dist/
dist_*/ dist_*/
# custom # custom
**/.claude/settings.local.json

240
changelog.md Normal file
View File

@@ -0,0 +1,240 @@
# Changelog
## 2025-12-15 - 1.11.0 - feat(commit)
Integrate DualAgentOrchestrator for commit message generation and improve diff/context handling
- Add @push.rocks/smartagent dependency and export it from plugins
- Use DualAgentOrchestrator to generate and guardian-validate commit messages
- Use DualAgentOrchestrator for changelog generation with guardian validation
- Switch commit flow to TaskContextFactory and DiffProcessor for token-efficient context
- Expose getOpenaiToken() and wire orchestrator with the project OpenAI token
- Enhance iterative context builder and context components to better manage token budgets and sampling
- Update npmextra.json with release config for @git.zone/cli and reference local smartagent package in package.json
## 2025-12-02 - 1.10.0 - feat(diff-processor)
Improve diff sampling and file prioritization: increase inclusion thresholds, expand sampled context, and boost priority for interface/type and entry-point files
- Raise small/medium file thresholds used by DiffProcessor (smallFileLines 50 -> 300, mediumFileLines 200 -> 800) so more source files are included fully or summarized rather than treated as large metadata-only files
- Increase sample window for medium files (sampleHeadLines/sampleTailLines 20 -> 75) to provide more context when summarizing diffs
- Boost importance scoring for interfaces/type files and entry points (adds +20 for interfaces/.types and +15 for index/mod entry files) to prioritize critical API surface in diff processing
- Keep other prioritization rules intact (source/test/config/docs/build heuristics), and align the aidoc commit DiffProcessor usage with the new defaults
## 2025-11-04 - 1.9.2 - fix(deps)
Update dependencies and devDependencies to newer versions (bump multiple packages)
- Bumped devDependencies: @git.zone/tsbuild 2.6.8 -> 2.7.1, @git.zone/tsrun 1.2.46 -> 1.6.2, @git.zone/tstest 2.3.6 -> 2.7.0
- Bumped runtime dependencies: @push.rocks/smartai 0.5.11 -> 0.8.0, @push.rocks/smartcli 4.0.11 -> 4.0.19, @push.rocks/smartgit 3.2.1 -> 3.3.1, @push.rocks/smartlog 3.1.9 -> 3.1.10, gpt-tokenizer 3.0.1 -> 3.2.0, typedoc 0.28.12 -> 0.28.14, typescript 5.9.2 -> 5.9.3
- No source code changes in this commit; dependency-only updates. Run the test suite and CI to verify compatibility.
## 2025-11-04 - 1.9.1 - fix(iterative-context-builder)
Rely on DiffProcessor for git diff pre-processing; remove raw char truncation, raise diff token safety, and improve logging
- Removed raw character-based truncation of additionalContext — diffs are expected to be pre-processed by DiffProcessor instead of blind substring truncation.
- Now validates pre-processed diff token count only and treats DiffProcessor as the primary sampler (DiffProcessor typically uses a ~100k token budget).
- Increased MAX_DIFF_TOKENS safety net to 200,000 to cover edge cases and avoid false positives; updated logs to reflect pre-processed diffs.
- Improved error messaging to indicate a likely DiffProcessor misconfiguration when pre-processed diffs exceed the safety limit.
- Updated informational logs to state that a pre-processed git diff was added to context.
## 2025-11-04 - 1.9.0 - feat(context)
Add intelligent DiffProcessor to summarize and prioritize git diffs and integrate it into the commit context pipeline
- Add DiffProcessor (ts/context/diff-processor.ts) to intelligently process git diffs: include small files fully, summarize medium files (head/tail sampling), and mark very large files as metadata-only to stay within token budgets.
- Integrate DiffProcessor into commit workflow (ts/aidocs_classes/commit.ts): preprocess raw diffs, emit processed diff statistics, and pass a token-efficient diff section into the TaskContextFactory for commit context generation.
- Export DiffProcessor and its types through the context index and types (ts/context/index.ts, ts/context/types.ts) so other context components can reuse it.
- Add comprehensive tests for the DiffProcessor behavior and integration (test/test.diffprocessor.node.ts) covering small/medium/large diffs, added/deleted files, prioritization, token budgets, and formatting for context.
- Minor adjustments across context/task factories and builders to accept and propagate processed diff strings rather than raw diffs, reducing risk of token overflows during iterative context building.
## 2025-11-04 - 1.8.3 - fix(context)
Prevent enormous git diffs and OOM during context building by adding exclusion patterns, truncation, and diagnostic logging
- Add comprehensive git diff exclusion globs (locks, build artifacts, maps, bundles, IDE folders, logs, caches) when collecting uncommitted diffs to avoid noisy/huge diffs
- Pass glob patterns directly to smartgit.getUncommittedDiff for efficient server-side matching
- Emit diagnostic statistics for diffs (files changed, total characters, estimated tokens, number of exclusion patterns) and warn on unusually large diffs
- Introduce pre-tokenization safety checks in iterative context builder: truncate raw diff text if it exceeds MAX_DIFF_CHARS and throw a clear error if token count still exceeds MAX_DIFF_TOKENS
- Format and log token counts using locale-aware formatting for clarity
- Improve robustness of commit context generation to reduce risk of OOM / model-limit overruns
## 2025-11-03 - 1.8.0 - feat(context)
Wire OpenAI provider through task context factory and add git-diff support to iterative context builder
- Pass AiDoc.openaiInstance through TaskContextFactory into IterativeContextBuilder to reuse the same OpenAI provider and avoid reinitialization.
- IterativeContextBuilder now accepts an optional OpenAiProvider and an additionalContext string; when provided, git diffs (or other extra context) are prepended to the AI context and token counts are updated.
- createContextForCommit now forwards the git diff into the iterative builder so commit-specific context includes the diff.
- Updated aidocs_classes (commit, description, readme) to supply the existing openaiInstance when creating the TaskContextFactory.
## 2025-11-03 - 1.7.0 - feat(IterativeContextBuilder)
Add iterative AI-driven context builder and integrate into task factory; add tests and iterative configuration
- Introduce IterativeContextBuilder: iterative, token-aware context construction that asks the AI which files to load and evaluates context sufficiency.
- Switch TaskContextFactory to use IterativeContextBuilder for readme, description and commit tasks (replaces earlier EnhancedContext flow for these tasks).
- Add iterative configuration options (maxIterations, firstPassFileLimit, subsequentPassFileLimit, temperature, model) in types and ConfigManager and merge support for user config.
- Update CLI (tokens and aidoc flows) to use the iterative context factory and improve task handling and messaging.
- Add test coverage: test/test.iterativecontextbuilder.node.ts to validate initialization, iterative builds, token budget respect and multiple task types.
- Enhance ContextCache, LazyFileLoader, ContextAnalyzer and ContextTrimmer to support the iterative pipeline and smarter prioritization/prompts.
## 2025-11-03 - 1.6.1 - fix(context)
Improve context building, caching and test robustness
- EnhancedContext: refactored smart context building to use the analyzer and TaskContextFactory by default; taskType now defaults to 'description' and task-specific modes are applied.
- ConfigManager: simplified analyzer configuration (removed enabled flag) and fixed getAnalyzerConfig fallback shape.
- ContextCache: more robust mtime handling and persistence; tests updated to use real file mtimes so cache validation works reliably.
- LazyFileLoader: adjusted token estimation tolerance and improved metadata caching behavior.
- ContextAnalyzer & trimming pipeline: improved prioritization and trimming integration to better enforce token budgets.
- Tests: relaxed strict timing/boolean checks and made assertions more tolerant (toEqual vs toBe) to reduce false negatives.
## 2025-11-02 - 1.6.0 - feat(context)
Introduce smart context system: analyzer, lazy loader, cache and README/docs improvements
- Add ContextAnalyzer for dependency-based file scoring and prioritization (PageRank-like centrality, relevance, efficiency, recency)
- Add LazyFileLoader to scan metadata and load files in parallel with lightweight token estimates
- Add ContextCache for persistent file content/token caching with TTL and max-size eviction
- Enhance ContextTrimmer with tier-based trimming and configurable light/aggressive levels
- Integrate new components into EnhancedContext and TaskContextFactory to build task-aware, token-optimized contexts
- Extend ConfigManager and types to support cache, analyzer, prioritization weights and tier configs (npmextra.json driven)
- Add comprehensive unit tests for ContextAnalyzer, ContextCache and LazyFileLoader
- Update README with Smart Context Building docs, examples, configuration options and CI workflow snippet
## 2025-09-07 - 1.5.2 - fix(package)
Bump dependencies, refine test script and imports, and overhaul README and docs
- Bumped multiple dependencies and devDependencies (including @git.zone/tspublish, @git.zone/tsbuild, @git.zone/tstest, @push.rocks/npmextra, @push.rocks/qenv, @push.rocks/smartfile, @push.rocks/smartlog, @push.rocks/smartshell, gpt-tokenizer, typedoc, etc.).
- Updated test script to run tstest with verbose, logfile and increased timeout; adjusted testCli script invocation.
- Fixed test import in test/test.aidoc.nonci.ts to use @git.zone/tstest tapbundle.
- Large README rewrite: reorganized and expanded content, added quick start, CLI commands, examples, configuration, troubleshooting and usage sections.
- Minor clarification added to commit prompt in ts/aidocs_classes/commit.ts (text cleanup and guidance).
## 2025-08-16 - 1.5.1 - fix(aidoc)
Bump dependencies, add pnpm workspace config, and add AiDoc.stop()
- Bumped multiple dependencies and devDependencies in package.json (notable upgrades: @git.zone/tsbuild, @git.zone/tspublish, @push.rocks/npmextra, @push.rocks/qenv, @push.rocks/smartai, @push.rocks/smartfile, @push.rocks/smartgit, @push.rocks/smartlog, @push.rocks/smartpath, @push.rocks/smartshell, typedoc, typescript).
- Added pnpm-workspace.yaml with onlyBuiltDependencies (esbuild, mongodb-memory-server, puppeteer, sharp).
- Added AiDoc.stop() to properly stop the OpenAI provider (resource/client shutdown).
- Updated packageManager field in package.json to a newer pnpm version/hash.
## 2025-05-14 - 1.5.0 - feat(docs)
Update project metadata and documentation to reflect comprehensive AI-enhanced features and improved installation and usage instructions
- Revised descriptions in package.json and npmextra.json to emphasize comprehensive documentation capabilities
- Expanded README with detailed installation options and extended usage examples for both CLI and API-like integrations
- Added new dependency (gpt-tokenizer) to support token counting for AI context building
- Adjusted keywords to better reflect project functionalities such as commit message automation and context trimming
## 2025-05-13 - 1.4.5 - fix(dependencies)
Upgrade various dependency versions and update package manager configuration
- Bump @git.zone/tsbuild from ^2.1.80 to ^2.3.2
- Upgrade @push.rocks/tapbundle from ^5.0.23 to ^6.0.3
- Update @types/node from ^22.8.1 to ^22.15.17
- Bump @push.rocks/smartai from ^0.4.2 to ^0.5.4
- Upgrade @push.rocks/smartlog from ^3.0.7 to ^3.0.9
- Update typedoc from ^0.27.9 to ^0.28.4
- Bump typescript from ^5.5.2 to ^5.8.3
- Add packageManager field with pnpm@10.10.0 specification
## 2025-02-25 - 1.4.4 - fix(dependencies)
Update dependencies to latest versions
- Updated '@push.rocks/smartai' from '^0.0.17' to '^0.4.2'
- Updated 'typedoc' from '^0.26.1' to '^0.27.9'
## 2025-01-14 - 1.4.3 - fix(aidocs_classes)
Improve readme generation instructions to ensure correct markdown formatting.
- Added guidance to avoid using backticks at the beginning and end of readme generation to prevent markdown issues.
- Clarified that the output is directly written to readme.md and backticks should only be used for code blocks.
## 2024-10-28 - 1.4.2 - fix(cli)
Ensure async completion for aidoc readme and description generation
- Added await statements for asynchronous methods buildReadme and buildDescription in the aidoc command.
## 2024-10-28 - 1.4.1 - fix(readme)
Correct async call to getModuleSubDirs in readme generation.
- Fixed an issue with asynchronous handling in readme generation for submodules.
- Ensured that getModuleSubDirs function is called with await to handle promises properly.
## 2024-10-28 - 1.4.0 - feat(aidocs)
Added support for building readmes for sub-modules in aidocs
- Updated the `Readme` class to handle monorepo projects by generating readmes for sub-modules.
- Integrated `tspublish` to identify sub-modules for readme generation.
## 2024-06-24 - 1.3.12 - fix(aidocs)
Fix changelog generation by handling leading newlines
- Fixed handling of leading newlines in the changelog to ensure proper formatting.
## 2024-06-23 - 1.3.11 - fix(core)
Fixed new changelog formatting issue to retain consistent spacing.
- Adjusted the new changelog generation to ensure consistent spacing for improved readability.
## 2024-06-23 - 1.3.10 - fix(aidocs_classes)
Fix changelog format to remove extra newline
- Updated `ts/aidocs_classes/commit.ts` to fix the changelog format.
## 2024-06-23 - 1.3.9 - fix(aidoc)
Fix changelog generation by properly stripping markdown code fences
- Corrected the changelog generation code to ensure markdown code fences are properly stripped.
## 2024-06-23 - 1.3.8 - fix(changelog)
Fix changelog generation by properly stripping markdown code fences
- Corrected the changelog generation code to ensure markdown code fences are properly stripped.
## 2024-06-23 - 1.3.7 - fix(aidoc)
Update to include package-lock.json in uncommitted changes check
- Modified the getUncommittedDiff method call in commit.ts to include package-lock.json along with pnpm-lock.yaml
## 2024-06-23 - 1.3.6 - fix(commit)
Fixed issue with retrieving uncommitted diffs in git repository
- Revised logic to correctly handle uncommitted changes by using an array for `getUncommittedDiff` method
- Ensured proper handling and representation of uncommitted changes in the output
## 2024-06-23 - 1.3.5 - fix(aidocs_classes)
Refactor and enhance changelog formatting
- Updated the `commit.ts` file to improve the changelog formatting and ensure consistency.
- Enhanced the changelog instructions to include summarizing messages for omitted commits.
- Removed unnecessary console logging in `projectcontext.ts`.
```markdown
## 2024-06-23 - 1.3.3 - fix(aidocs_classes)
Fix changelog formatting issue in commit class
## 2024-06-23 - 1.3.2 - fix(aidocs_classes)
Fix minor bugs and update dependencies in aidocs_classes
## 2024-06-23 - 1.3.1 - fix(aidocs_classes)
Fix typo in INextCommitObject interface and update date format in changelog generation.
## 2024-06-23 - 1.3.0 - fix(aidocs_classes)
Fix typo in INextCommitObject interface
## 2024-06-23 - 1.2.4 - feat(core)
Added smarttime dependency and improved changelog generation
## 2024-06-23 - 1.2.3 - fix(logging)
Refactor logger initialization to use commitinfo data
## 2024-06-23 - 1.2.2 - fix(aidocs)
Fix bug in AiDoc class causing undefined token handling
## 2024-06-23 - 1.2.0 - fix(core)
Fixed usage of plugins in project context and readme generation
## 2024-06-23 - 1.1.42 - feat(aidocs_classes)
Enhance changelog generation by supporting complete generation in the absence of previous changelog files
## 2024-06-23 - 1.1.41 - fix(aidocs_classes)
Improve commit message generation by handling empty diffs and updating changelog instructions
```

View File

@@ -9,22 +9,19 @@
"npmPackagename": "@git.zone/tsdoc", "npmPackagename": "@git.zone/tsdoc",
"license": "MIT", "license": "MIT",
"projectDomain": "git.zone", "projectDomain": "git.zone",
"description": "An advanced TypeScript documentation tool leveraging AI for enhanced insights and automated documentation generation, with capabilities for automated and enhanced documentation creation tailor-made for TypeScript projects.", "description": "A comprehensive TypeScript documentation tool that leverages AI to generate and enhance project documentation, including dynamic README creation, API docs via TypeDoc, and smart commit message generation.",
"keywords": [ "keywords": [
"TypeScript", "TypeScript",
"documentation", "documentation",
"AI-enhanced documentation", "AI",
"automated documentation generation", "CLI",
"README",
"TypeDoc",
"commit messages",
"automation",
"code analysis", "code analysis",
"development tool", "context trimming",
"CLI utility", "developer tools"
"API documentation",
"developer productivity",
"code insights",
"integrated development environment tooling",
"code quality enhancement",
"project documentation",
"documentation automation"
] ]
} }
}, },
@@ -34,5 +31,14 @@
}, },
"tsdoc": { "tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n" "legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@git.zone/cli": {
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
} }
} }

View File

@@ -1,43 +1,49 @@
{ {
"name": "@git.zone/tsdoc", "name": "@git.zone/tsdoc",
"version": "1.1.25", "version": "1.11.3",
"private": false, "private": false,
"description": "An advanced TypeScript documentation tool leveraging AI for enhanced insights and automated documentation generation, with capabilities for automated and enhanced documentation creation tailor-made for TypeScript projects.", "description": "A comprehensive TypeScript documentation tool that leverages AI to generate and enhance project documentation, including dynamic README creation, API docs via TypeDoc, and smart commit message generation.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
"author": "Lossless GmbH", "exports": {
".": "./dist_ts/index.js"
},
"author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"tsdoc": "cli.js" "tsdoc": "cli.js"
}, },
"scripts": { "scripts": {
"test": "(tstest test/) && npm run testCli", "test": "(tstest test/ --verbose --logfile --timeout 600) && npm run testCli",
"testCli": "(node ./cli.ts.js) && (node ./cli.ts.js aidocs)", "testCli": "(node ./cli.ts.js) && (node ./cli.ts.js aidocs)",
"build": "(tsbuild --web --allowimplicitany)" "build": "(tsbuild --web --allowimplicitany)",
"buildDocs": "tsdoc"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.1.65", "@git.zone/tsbuild": "^4.0.2",
"@git.zone/tsrun": "^1.2.46", "@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^1.0.73", "@git.zone/tstest": "^3.1.3",
"@push.rocks/tapbundle": "^5.0.4", "@types/node": "^25.0.2"
"@types/node": "^20.12.7"
}, },
"dependencies": { "dependencies": {
"@push.rocks/early": "^4.0.3", "@git.zone/tspublish": "^1.10.3",
"@push.rocks/npmextra": "^5.0.12", "@push.rocks/early": "^4.0.4",
"@push.rocks/qenv": "^6.0.5", "@push.rocks/npmextra": "^5.3.3",
"@push.rocks/smartai": "^0.0.8", "@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartcli": "^4.0.10", "@push.rocks/smartagent": "1.2.5",
"@push.rocks/smartai": "^0.8.0",
"@push.rocks/smartcli": "^4.0.19",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile": "^11.0.13", "@push.rocks/smartfile": "^13.1.2",
"@push.rocks/smartinteract": "^2.0.15", "@push.rocks/smartfs": "^1.2.0",
"@push.rocks/smartlog": "^3.0.1", "@push.rocks/smartgit": "^3.3.1",
"@push.rocks/smartlog-destination-local": "^9.0.1", "@push.rocks/smartinteract": "^2.0.16",
"@push.rocks/smartpath": "^5.0.14", "@push.rocks/smartlog": "^3.1.10",
"@push.rocks/smartshell": "^3.0.4", "@push.rocks/smartlog-destination-local": "^9.0.2",
"typedoc": "^0.25.13", "@push.rocks/smartpath": "^6.0.0",
"typescript": "^5.4.5" "@push.rocks/smartshell": "^3.3.0",
"@push.rocks/smarttime": "^4.1.1",
"typedoc": "^0.28.15",
"typescript": "^5.9.3"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@@ -57,17 +63,23 @@
"keywords": [ "keywords": [
"TypeScript", "TypeScript",
"documentation", "documentation",
"AI-enhanced documentation", "AI",
"automated documentation generation", "CLI",
"README",
"TypeDoc",
"commit messages",
"automation",
"code analysis", "code analysis",
"development tool", "context trimming",
"CLI utility", "developer tools"
"API documentation", ],
"developer productivity", "repository": {
"code insights", "type": "git",
"integrated development environment tooling", "url": "git+https://gitlab.com/gitzone/tsdoc.git"
"code quality enhancement", },
"project documentation", "bugs": {
"documentation automation" "url": "https://gitlab.com/gitzone/tsdoc/issues"
] },
"homepage": "https://gitlab.com/gitzone/tsdoc#readme",
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
} }

14151
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

419
readme.md
View File

@@ -1,108 +1,407 @@
# @git.zone/tsdoc # @git.zone/tsdoc 🚀
An advanced TypeScript documentation tool that leverages AI for enhanced insights and automated documentation generation.
## Install AI-Powered Documentation for TypeScript Projects
To install `@git.zone/tsdoc`, you have two options depending on your usage preference: globally for CLI use or locally within your project for use through NPX. To install globally, run:
```sh ## Issue Reporting and Security
npm install -g @git.zone/tsdoc
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
## What is tsdoc?
`@git.zone/tsdoc` is a next-generation documentation CLI tool that combines traditional TypeDoc generation with cutting-edge AI to create comprehensive, intelligent documentation for your TypeScript projects. It reads your code, understands it, and writes documentation that actually makes sense.
### ✨ Key Features
- **🤖 AI-Enhanced Documentation** - Leverages AI to generate contextual READMEs
- **🧠 Smart Context Building** - Intelligent file prioritization with dependency analysis and caching
- **📚 TypeDoc Integration** - Classic API documentation generation when you need it
- **💬 Smart Commit Messages** - AI analyzes your changes and suggests meaningful commit messages
- **🎯 Context Optimization** - Advanced token management with 40-60% reduction in usage
- **⚡ Performance Optimized** - 3-5x faster with lazy loading and parallel processing
- **📦 Zero Config** - Works out of the box with sensible defaults
- **🔧 Highly Configurable** - Customize every aspect when needed
## Installation
```bash
# Global installation (recommended)
pnpm add -g @git.zone/tsdoc
# Or use with npx
npx @git.zone/tsdoc
``` ```
For local installation within your project, use: ## Quick Start
```sh ### Generate AI-Powered Documentation
npm install --save @git.zone/tsdoc
```bash
# In your project root
tsdoc aidoc
``` ```
You can then use the tool through NPX if installed locally: That's it! tsdoc will analyze your entire codebase and generate:
- A comprehensive README.md
- Updated package.json description and keywords
- Smart documentation based on your actual code structure
```sh ### Generate Traditional TypeDoc
npx tsdoc
```bash
tsdoc typedoc --publicSubdir docs
``` ```
## Usage ### Get Smart Commit Messages
`@git.zone/tsdoc` is a tool designed to improve the process of generating documentation for TypeScript projects. It combines the capabilities of Typedoc with AI-powered features to automatically generate insights and enhance the quality of your API documentation without manual intervention.
To get started, after installation, navigate to the root directory of your TypeScript project where your `tsconfig.json` is located and run: ```bash
tsdoc commit
```typescript
import { runCli } from '@git.zone/tsdoc';
// Initialize and run the CLI
runCli().then(() => {
console.log('Documentation generation complete!');
}).catch((error) => {
console.error('Failed to generate documentation:', error);
});
``` ```
### Generating Documentation ## CLI Commands
`@git.zone/tsdoc` provides a command-line interface to generate documentation directly from your TypeScript source files. The CLI uses information from your TypeScript configuration and the source files to create comprehensive documentation.
Once `tsdoc` is installed globally, you can run the following command in the root of your TypeScript project: | Command | Description |
|---------|-------------|
| `tsdoc` | Auto-detects and runs appropriate documentation |
| `tsdoc aidoc` | Generate AI-enhanced documentation |
| `tsdoc typedoc` | Generate TypeDoc documentation |
| `tsdoc commit` | Generate smart commit message |
| `tsdoc tokens` | Analyze token usage for AI context |
```sh ### Token Analysis
tsdoc
Understanding token usage helps optimize AI costs:
```bash
# Show token count for current project
tsdoc tokens
# Show detailed stats for all task types
tsdoc tokens --all
# Show detailed breakdown with file listing
tsdoc tokens --detailed --listFiles
``` ```
This command analyzes your TypeScript project, extracts type information, and generates documentation pages as HTML or Markdown. You can customize the output format and specify additional options via command line parameters or by editing `tsdoc` configuration files. ### Command Options
### Command Line Parameters #### tsdoc aidoc
The CLI tool reads options from `./ts/cli.ts`. Command line parameters allow you to customize the behavior of `tsdoc`. For more detailed usage, run: - `--tokens` / `--showTokens` - Show token count before generating
- `--tokensOnly` - Only show token count, don't generate
```sh #### tsdoc typedoc
tsdoc --help - `--publicSubdir <dir>` - Output subdirectory within public folder
#### tsdoc tokens
- `--task <type>` - Specify task type: `readme`, `commit`, or `description`
- `--all` - Show stats for all task types
- `--detailed` - Show detailed token usage and costs
- `--listFiles` - List all files included in context
- `--model <name>` - Show usage for specific model (`gpt4`, `gpt35`)
## Configuration
Configure tsdoc via `npmextra.json`:
```json
{
"@git.zone/tsdoc": {
"legal": "## License and Legal Information\n\n...",
"context": {
"maxTokens": 190000,
"defaultMode": "trimmed",
"cache": {
"enabled": true,
"ttl": 3600,
"maxSize": 100
},
"analyzer": {
"useAIRefinement": false
},
"prioritization": {
"dependencyWeight": 0.3,
"relevanceWeight": 0.4,
"efficiencyWeight": 0.2,
"recencyWeight": 0.1
},
"tiers": {
"essential": { "minScore": 0.8, "trimLevel": "none" },
"important": { "minScore": 0.5, "trimLevel": "light" },
"optional": { "minScore": 0.2, "trimLevel": "aggressive" }
},
"taskSpecificSettings": {
"readme": {
"mode": "trimmed",
"includePaths": ["ts/", "src/"],
"excludePaths": ["test/", "node_modules/"]
},
"commit": {
"mode": "trimmed",
"focusOnChangedFiles": true
}
},
"trimming": {
"removeImplementations": true,
"preserveInterfaces": true,
"preserveJSDoc": true,
"maxFunctionLines": 5,
"removeComments": true
}
}
}
}
``` ```
This will display a list of available commands and options, such as specifying the output directory for the generated documentation or enabling/disabling certain features of the documentation generator. ### Configuration Options
### Examples #### Context Settings
Below is an example of how to use `tsdoc` to generate documentation with custom options: - **maxTokens** - Maximum tokens for AI context (default: 190000)
- **defaultMode** - Default context mode: 'full', 'trimmed', or 'summarized'
- **cache** - Caching configuration for improved performance
- **analyzer** - Smart file analysis and prioritization settings
- **prioritization** - Weights for file importance scoring
- **tiers** - Tier thresholds and trimming levels
```sh #### Cache Configuration
tsdoc --output docs/api --format html - **enabled** - Enable/disable file caching (default: true)
``` - **ttl** - Time-to-live in seconds (default: 3600)
This command generates HTML documentation for your project and places it in the `docs/api` directory. - **maxSize** - Maximum cache size in MB (default: 100)
- **directory** - Cache directory path (default: .nogit/context-cache)
### Additional Features ## How It Works
Beyond basic documentation generation, `tsdoc` integrates AI-powered analysis to enrich the documentation automatically. This feature helps by providing insights into complex types, documenting patterns used within your codebase, and suggesting improvements for better maintainability and readability of your documentation.
**Note:** `tsdoc` is designed for use with projects adhering to modern TypeScript conventions. Ensure your project structure and TypeScript configuration are compatible for optimal results. ### 🚀 Smart Context Building Pipeline
### Integration with CI/CD 1. **📊 Fast Metadata Scanning** - Lazy loading scans files without reading contents
`@git.zone/tsdoc` can be integrated into your CI/CD pipelines to automatically generate and update documentation as part of your build process. This ensures that your API documentation is always up-to-date with your codebase. 2. **🧬 Dependency Analysis** - Builds dependency graph from import statements
3. **🎯 Intelligent Scoring** - Multi-factor importance scoring:
- **Relevance**: Task-specific file importance (e.g., index.ts for READMEs)
- **Centrality**: How many files depend on this file
- **Efficiency**: Information density (tokens vs. value)
- **Recency**: Recently changed files (for commits)
4. **🏆 Smart Prioritization** - Files sorted by combined importance score
5. **🎭 Tier-Based Trimming** - Adaptive trimming based on importance:
- **Essential** (score ≥ 0.8): No trimming
- **Important** (score ≥ 0.5): Light trimming
- **Optional** (score ≥ 0.2): Aggressive trimming
6. **💾 Intelligent Caching** - Cache results with file change detection
7. **🧠 AI Processing** - Send optimized context to AI for documentation
Here's an example configuration snippet for a CI workflow: ### Context Optimization Benefits
```yml The smart context system delivers significant improvements:
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| **Token Usage** | ~190k (limit) | ~110-130k | ⬇️ 40-60% reduction |
| **Build Time** | 4-6 seconds | 1-2 seconds | ⚡ 3-5x faster |
| **Memory Usage** | All files loaded | Metadata + selected | 📉 80%+ reduction |
| **Relevance** | Alphabetical sorting | Smart scoring | 🎯 90%+ relevant |
| **Cache Hits** | None | 70-80% | 🚀 Major speedup |
## Environment Variables
| Variable | Description |
|----------|-------------|
| `OPENAI_TOKEN` | Your OpenAI API key for AI features (required) |
The token can also be provided interactively on first run - it will be persisted in `~/.npmextra/kv/@git.zone/tsdoc.json`.
## Use Cases
### 🚀 Continuous Integration
```yaml
# .github/workflows/docs.yml
name: Documentation
on: [push]
jobs:
docs:
runs-on: ubuntu-latest
steps: steps:
- name: Install tsdoc - uses: actions/checkout@v3
run: npm install -g @git.zone/tsdoc - name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Generate Documentation - name: Generate Documentation
run: tsdoc env:
OPENAI_TOKEN: ${{ secrets.OPENAI_TOKEN }}
run: |
npm install -g @git.zone/tsdoc
tsdoc aidoc
- name: Commit Changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add readme.md package.json
git commit -m "docs: update documentation [skip ci]" || exit 0
git push
``` ```
Remember to replace placeholders and adjust paths as necessary depending on your CI provider and project configuration. ### 🔄 Pre-Commit Hooks
### Conclusion ```bash
`@git.zone/tsdoc` is a powerful tool that leverages the best of Typedoc and AI to streamline the documentation process for TypeScript projects. By automating the generation of insightful and comprehensive documentation, it enhances developer productivity and improves the quality of project documentation. # .git/hooks/prepare-commit-msg
#!/bin/bash
tsdoc commit > .git/COMMIT_EDITMSG
```
### 📦 Package Publishing
```json
{
"scripts": {
"prepublishOnly": "tsdoc aidoc",
"version": "tsdoc aidoc && git add readme.md"
}
}
```
## Requirements
- **Node.js** >= 18.0.0
- **TypeScript** project
- **OpenAI API key** (for AI features)
## Troubleshooting
### Token Limit Exceeded
If you hit token limits, try:
```bash
# Check token usage details
tsdoc tokens --all --detailed
```
Or configure stricter limits in `npmextra.json`:
```json
{
"@git.zone/tsdoc": {
"context": {
"maxTokens": 100000,
"tiers": {
"essential": { "minScore": 0.9, "trimLevel": "none" },
"important": { "minScore": 0.7, "trimLevel": "aggressive" },
"optional": { "minScore": 0.5, "trimLevel": "aggressive" }
}
}
}
}
```
### Missing API Key
Set your OpenAI key:
```bash
export OPENAI_TOKEN="your-key-here"
tsdoc aidoc
```
### Slow Performance
Enable caching and adjust settings in `npmextra.json`:
```json
{
"@git.zone/tsdoc": {
"context": {
"cache": {
"enabled": true,
"ttl": 7200,
"maxSize": 200
}
}
}
}
```
### Cache Issues
Clear the cache if needed:
```bash
rm -rf .nogit/context-cache
```
## Why tsdoc?
### 🎯 Actually Understands Your Code
Not just parsing, but real comprehension through AI. The smart context system ensures AI sees the most relevant parts of your codebase.
### ⏱️ Saves Hours
Generate complete, accurate documentation in seconds. The intelligent caching system makes subsequent runs even faster.
### 🔄 Always Up-to-Date
Regenerate documentation with every change. Smart dependency analysis ensures nothing important is missed.
### 🎨 Beautiful Output
Clean, professional documentation every time. AI understands your code's purpose and explains it clearly.
### 💰 Cost-Effective
Smart context optimization reduces AI API costs by 40-60% without sacrificing quality.
## Architecture
### Core Components
```
@git.zone/tsdoc
├── AiDoc # Main AI documentation orchestrator
├── TypeDoc # Traditional TypeDoc integration
├── Context System # Smart context building
│ ├── EnhancedContext # Main context builder
│ ├── LazyFileLoader # Efficient file loading
│ ├── ContextCache # Performance caching
│ ├── ContextAnalyzer # Intelligent file analysis
│ ├── ContextTrimmer # Adaptive code trimming
│ ├── DiffProcessor # Git diff optimization
│ ├── ConfigManager # Configuration management
│ └── TaskContextFactory # Task-specific contexts
└── CLI # Command-line interface
```
### Data Flow
```
Project Files
LazyFileLoader (metadata scan)
ContextAnalyzer (scoring & prioritization)
ContextCache (check cache)
File Loading (parallel, on-demand)
ContextTrimmer (tier-based)
Token Budget (enforcement)
AI Model
Generated Documentation
```
## License and Legal Information ## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks ### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH. This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
### Company Information ### Company Information
Task Venture Capital GmbH Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc. For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works. By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

314
readme.plan.md Normal file
View File

@@ -0,0 +1,314 @@
# TSDocs Context Optimization Plan
## Problem Statement
For large TypeScript projects, the context generated for AI-based documentation creation becomes too large, potentially exceeding even o4-mini's 200K token limit. This affects the ability to effectively generate:
- Project documentation (README.md)
- API descriptions and keywords
- Commit messages and changelogs
Current implementation simply includes all TypeScript files and key project files, but lacks intelligent selection, prioritization, or content reduction mechanisms.
## Analysis of Approaches
### 1. Smart Content Selection
**Description:** Intelligently select only files that are necessary for the specific task being performed, using heuristic rules.
**Advantages:**
- Simple to implement
- Predictable behavior
- Can be fine-tuned for different operations
**Disadvantages:**
- Requires manual tuning of rules
- May miss important context in complex projects
- Static approach lacks adaptability
**Implementation Complexity:** Medium
### 2. File Prioritization
**Description:** Rank files by relevance using git history, file size, import/export analysis, and relationship to the current task.
**Advantages:**
- Adaptively includes the most relevant files first
- Maintains context for frequently changed or central files
- Can leverage git history for additional signals
**Disadvantages:**
- Complexity in determining accurate relevance scores
- Requires analyzing project structure
- May require scanning imports/exports for dependency analysis
**Implementation Complexity:** High
### 3. Chunking Strategy
**Description:** Process the project in logical segments, generating intermediate results that are then combined to create the final output.
**Advantages:**
- Can handle projects of any size
- Focused context for each specific part
- May improve quality by focusing on specific areas deeply
**Disadvantages:**
- Complex orchestration of multiple AI calls
- Challenge in maintaining consistency across chunks
- May increase time and cost for processing
**Implementation Complexity:** High
### 4. Dynamic Context Trimming
**Description:** Automatically reduce context by removing non-essential code while preserving structure. Techniques include:
- Removing implementation details but keeping interfaces and type definitions
- Truncating large functions while keeping signatures
- Removing comments and whitespace (except JSDoc)
- Keeping only imports/exports for context files
**Advantages:**
- Preserves full project structure
- Flexible token usage based on importance
- Good balance between completeness and token efficiency
**Disadvantages:**
- Potential to remove important implementation details
- Risk of missing context needed for specific tasks
- Complex rules for what to trim vs keep
**Implementation Complexity:** Medium
### 5. Embeddings-Based Retrieval
**Description:** Create vector embeddings of project files and retrieve only the most relevant ones for a specific task using semantic similarity.
**Advantages:**
- Highly adaptive to different types of requests
- Leverages semantic understanding of content
- Can scale to extremely large projects
**Disadvantages:**
- Requires setting up and managing embeddings database
- Added complexity of running vector similarity searches
- Higher resource requirements for maintaining embeddings
**Implementation Complexity:** Very High
### 6. Task-Specific Contexts
**Description:** Create separate optimized contexts for different tasks (readme, commit messages, etc.) with distinct file selection and processing strategies.
**Advantages:**
- Highly optimized for each specific task
- Efficient token usage for each operation
- Improved quality through task-focused contexts
**Disadvantages:**
- Maintenance of multiple context building strategies
- More complex configuration
- Potential duplication in implementation
**Implementation Complexity:** Medium
### 7. Recursive Summarization
**Description:** Summarize larger files first, then include these summaries in the final context along with smaller files included in full.
**Advantages:**
- Can handle arbitrary project sizes
- Preserves essential information from all files
- Balanced approach to token usage
**Disadvantages:**
- Quality loss from summarization
- Increased processing time from multiple AI calls
- Complex orchestration logic
**Implementation Complexity:** High
## Implementation Strategy
We propose a phased implementation approach, starting with the most impactful and straightforward approaches, then building toward more complex solutions as needed:
### Phase 1: Foundation (1-2 weeks)
1. **Implement Dynamic Context Trimming**
- Create a `ContextProcessor` class that takes SmartFile objects and applies trimming rules
- Implement configurable trimming rules (remove implementations, keep signatures)
- Add a configuration option to control trimming aggressiveness
- Support preserving JSDoc comments while removing other comments
2. **Enhance Token Monitoring**
- Track token usage per file to identify problematic files
- Implement token budgeting to stay within limits
- Add detailed token reporting for optimization
### Phase 2: Smart Selection (2-3 weeks)
3. **Implement Task-Specific Contexts**
- Create specialized context builders for readme, commit messages, and descriptions
- Customize file selection rules for each task
- Add configuration options for task-specific settings
4. **Add Smart Content Selection**
- Implement heuristic rules for file importance
- Create configuration for inclusion/exclusion patterns
- Add ability to focus on specific directories or modules
### Phase 3: Advanced Techniques (3-4 weeks)
5. **Implement File Prioritization**
- Add git history analysis to identify frequently changed files
- Implement dependency analysis to identify central files
- Create a scoring system for file relevance
6. **Add Optional Recursive Summarization**
- Implement file summarization for large files
- Create a hybrid approach that mixes full files and summaries
- Add configuration to control summarization thresholds
### Phase 4: Research-Based Approaches (Future Consideration)
7. **Research and Evaluate Embeddings-Based Retrieval**
- Prototype embeddings creation for TypeScript files
- Evaluate performance and accuracy
- Implement if benefits justify the complexity
8. **Explore Chunking Strategies**
- Research effective chunking approaches for documentation
- Prototype and evaluate performance
- Implement if benefits justify the complexity
## Technical Design
### Core Components
1. **ContextBuilder** - Enhanced version of current ProjectContext
```typescript
interface IContextBuilder {
buildContext(): Promise<string>;
getTokenCount(): number;
setContextMode(mode: 'normal' | 'trimmed' | 'summarized'): void;
setTokenBudget(maxTokens: number): void;
setPrioritizationStrategy(strategy: IPrioritizationStrategy): void;
}
```
2. **FileProcessor** - Handles per-file processing and trimming
```typescript
interface IFileProcessor {
processFile(file: SmartFile): Promise<string>;
setProcessingMode(mode: 'full' | 'trim' | 'summarize'): void;
getTokenCount(): number;
}
```
3. **PrioritizationStrategy** - Ranks files by importance
```typescript
interface IPrioritizationStrategy {
rankFiles(files: SmartFile[], context: string): Promise<SmartFile[]>;
setImportanceMetrics(metrics: IImportanceMetrics): void;
}
```
4. **TaskContextFactory** - Creates optimized contexts for specific tasks
```typescript
interface ITaskContextFactory {
createContextForReadme(projectDir: string): Promise<string>;
createContextForCommit(projectDir: string, diff: string): Promise<string>;
createContextForDescription(projectDir: string): Promise<string>;
}
```
### Configuration Options
The system will support configuration via a new section in `npmextra.json`:
```json
{
"tsdoc": {
"context": {
"maxTokens": 190000,
"defaultMode": "dynamic",
"taskSpecificSettings": {
"readme": {
"mode": "full",
"includePaths": ["src/", "lib/"],
"excludePaths": ["test/", "examples/"]
},
"commit": {
"mode": "trimmed",
"focusOnChangedFiles": true
},
"description": {
"mode": "summarized",
"includePackageInfo": true
}
},
"trimming": {
"removeImplementations": true,
"preserveInterfaces": true,
"preserveTypeDefs": true,
"preserveJSDoc": true,
"maxFunctionLines": 5
}
}
}
}
```
## Cost-Benefit Analysis
### Cost Considerations
1. **Development costs**
- Initial implementation of foundational components (~30-40 hours)
- Testing and validation across different project sizes (~10-15 hours)
- Documentation and configuration examples (~5 hours)
2. **Operational costs**
- Potential increased processing time for context preparation
- Additional API calls for summarization or embeddings approaches
- Monitoring and maintenance of the system
### Benefits
1. **Scalability**
- Support for projects of any size, up to and beyond o4-mini's 200K token limit
- Future-proof design that can adapt to different models and token limits
2. **Quality improvements**
- More focused contexts lead to better AI outputs
- Task-specific optimization improves relevance
- Consistent performance regardless of project size
3. **User experience**
- Predictable behavior for all project sizes
- Transparent token usage reporting
- Configuration options for different usage patterns
## First Deliverable
For immediate improvements, we recommend implementing Dynamic Context Trimming and Task-Specific Contexts first, as these offer the best balance of impact and implementation complexity.
### Implementation Plan for Dynamic Context Trimming
1. Create a basic `ContextTrimmer` class that processes TypeScript files:
- Remove function bodies but keep signatures
- Preserve interface and type definitions
- Keep imports and exports
- Preserve JSDoc comments
2. Integrate with the existing ProjectContext class:
- Add a trimming mode option
- Apply trimming during the context building process
- Track and report token savings
3. Modify the CLI to support trimming options:
- Add a `--trim` flag to enable trimming
- Add a `--trim-level` option for controlling aggressiveness
- Show token usage with and without trimming
This approach could reduce token usage by 40-70% while preserving the essential structure of the codebase, making it suitable for large projects while maintaining high-quality AI outputs.

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as qenv from '@push.rocks/qenv'; import * as qenv from '@push.rocks/qenv';
let testQenv = new qenv.Qenv('./', '.nogit/'); let testQenv = new qenv.Qenv('./', '.nogit/');
@@ -8,20 +8,35 @@ let aidocs: tsdocs.AiDoc;
tap.test('should create an AIdocs class', async () => { tap.test('should create an AIdocs class', async () => {
aidocs = new tsdocs.AiDoc({ aidocs = new tsdocs.AiDoc({
'OPENAI_TOKEN': await testQenv.getEnvVarOnDemand('OPENAI_TOKEN') OPENAI_TOKEN: await testQenv.getEnvVarOnDemand('OPENAI_TOKEN'),
}); });
expect(aidocs).toBeInstanceOf(tsdocs.AiDoc); expect(aidocs).toBeInstanceOf(tsdocs.AiDoc);
}); });
tap.test('should start AIdocs', async () => { tap.test('should start AIdocs', async () => {
await aidocs.start(); await aidocs.start();
});
tap.skip.test('should start AIdocs', async () => {
await aidocs.buildReadme('./'); await aidocs.buildReadme('./');
}) });
tap.test('should start AIdocs', async () => { tap.skip.test('should start AIdocs', async () => {
await aidocs.start();
await aidocs.buildDescription('./'); await aidocs.buildDescription('./');
}) });
tap.test('should build commit object', async () => {
const commitObject = await aidocs.buildNextCommitObject('./');
console.log(commitObject);
expect(commitObject).not.toBeUndefined();
expect(commitObject).toHaveProperty('recommendedNextVersion');
expect(commitObject).toHaveProperty('recommendedNextVersionLevel');
expect(commitObject).toHaveProperty('recommendedNextVersionScope');
expect(commitObject).toHaveProperty('recommendedNextVersionMessage');
});
tap.test('should stop AIdocs', async () => {
await aidocs.stop();
});
tap.start(); tap.start();

View File

@@ -0,0 +1,304 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { DiffProcessor } from '../ts/classes.diffprocessor.js';
// Sample diff strings for testing
const createSmallDiff = (filepath: string, addedLines = 5, removedLines = 3): string => {
const lines: string[] = [];
lines.push(`--- a/${filepath}`);
lines.push(`+++ b/${filepath}`);
lines.push(`@@ -1,10 +1,12 @@`);
for (let i = 0; i < removedLines; i++) {
lines.push(`-removed line ${i + 1}`);
}
for (let i = 0; i < addedLines; i++) {
lines.push(`+added line ${i + 1}`);
}
lines.push(' unchanged line');
return lines.join('\n');
};
const createMediumDiff = (filepath: string): string => {
const lines: string[] = [];
lines.push(`--- a/${filepath}`);
lines.push(`+++ b/${filepath}`);
lines.push(`@@ -1,100 +1,150 @@`);
// 150 lines of changes
for (let i = 0; i < 75; i++) {
lines.push(`+added line ${i + 1}`);
}
for (let i = 0; i < 75; i++) {
lines.push(`-removed line ${i + 1}`);
}
return lines.join('\n');
};
const createLargeDiff = (filepath: string): string => {
const lines: string[] = [];
lines.push(`--- a/${filepath}`);
lines.push(`+++ b/${filepath}`);
lines.push(`@@ -1,1000 +1,1500 @@`);
// 2500 lines of changes
for (let i = 0; i < 1250; i++) {
lines.push(`+added line ${i + 1}`);
}
for (let i = 0; i < 1250; i++) {
lines.push(`-removed line ${i + 1}`);
}
return lines.join('\n');
};
const createDeletedFileDiff = (filepath: string): string => {
return `--- a/${filepath}
+++ /dev/null
@@ -1,5 +0,0 @@
-deleted line 1
-deleted line 2
-deleted line 3
-deleted line 4
-deleted line 5`;
};
const createAddedFileDiff = (filepath: string): string => {
return `--- /dev/null
+++ b/${filepath}
@@ -0,0 +1,5 @@
+added line 1
+added line 2
+added line 3
+added line 4
+added line 5`;
};
tap.test('DiffProcessor should parse small diff correctly', async () => {
const processor = new DiffProcessor();
const smallDiff = createSmallDiff('src/test.ts', 5, 3);
const result = processor.processDiffs([smallDiff]);
expect(result.totalFiles).toEqual(1);
expect(result.fullDiffs.length).toEqual(1);
expect(result.summarizedDiffs.length).toEqual(0);
expect(result.metadataOnly.length).toEqual(0);
expect(result.totalTokens).toBeGreaterThan(0);
});
tap.test('DiffProcessor should summarize medium diff', async () => {
const processor = new DiffProcessor();
const mediumDiff = createMediumDiff('src/medium-file.ts');
const result = processor.processDiffs([mediumDiff]);
expect(result.totalFiles).toEqual(1);
expect(result.fullDiffs.length).toEqual(0);
expect(result.summarizedDiffs.length).toEqual(1);
expect(result.metadataOnly.length).toEqual(0);
// Verify the summarized diff contains the sample
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('SUMMARIZED DIFFS');
expect(formatted).toInclude('lines omitted');
});
tap.test('DiffProcessor should handle large diff as metadata only', async () => {
const processor = new DiffProcessor();
const largeDiff = createLargeDiff('dist/bundle.js');
const result = processor.processDiffs([largeDiff]);
expect(result.totalFiles).toEqual(1);
expect(result.fullDiffs.length).toEqual(0);
expect(result.summarizedDiffs.length).toEqual(0);
expect(result.metadataOnly.length).toEqual(1);
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('METADATA ONLY');
expect(formatted).toInclude('dist/bundle.js');
});
tap.test('DiffProcessor should prioritize source files over build artifacts', async () => {
const processor = new DiffProcessor();
const diffs = [
createSmallDiff('dist/bundle.js'),
createSmallDiff('src/important.ts'),
createSmallDiff('build/output.js'),
createSmallDiff('src/core.ts'),
];
const result = processor.processDiffs(diffs);
expect(result.totalFiles).toEqual(4);
// Source files should be included fully first
const formatted = processor.formatForContext(result);
const srcImportantIndex = formatted.indexOf('src/important.ts');
const srcCoreIndex = formatted.indexOf('src/core.ts');
const distBundleIndex = formatted.indexOf('dist/bundle.js');
const buildOutputIndex = formatted.indexOf('build/output.js');
// Source files should appear before build artifacts
expect(srcImportantIndex).toBeLessThan(distBundleIndex);
expect(srcCoreIndex).toBeLessThan(buildOutputIndex);
});
tap.test('DiffProcessor should respect token budget', async () => {
const processor = new DiffProcessor({
maxDiffTokens: 500, // Very small budget to force metadata-only
});
// Create multiple large diffs that will exceed budget
const diffs = [
createLargeDiff('src/file1.ts'),
createLargeDiff('src/file2.ts'),
createLargeDiff('src/file3.ts'),
createLargeDiff('src/file4.ts'),
];
const result = processor.processDiffs(diffs);
expect(result.totalTokens).toBeLessThanOrEqual(500);
// With such a small budget and large files, most should be metadata only
expect(result.metadataOnly.length).toBeGreaterThanOrEqual(2);
});
tap.test('DiffProcessor should handle deleted files', async () => {
const processor = new DiffProcessor();
const deletedDiff = createDeletedFileDiff('src/old-file.ts');
const result = processor.processDiffs([deletedDiff]);
expect(result.totalFiles).toEqual(1);
// Small deleted file should be included fully
expect(result.fullDiffs.length).toEqual(1);
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('src/old-file.ts');
// Verify the file appears in the output
expect(formatted).toInclude('FULL DIFFS');
});
tap.test('DiffProcessor should handle added files', async () => {
const processor = new DiffProcessor();
const addedDiff = createAddedFileDiff('src/new-file.ts');
const result = processor.processDiffs([addedDiff]);
expect(result.totalFiles).toEqual(1);
// Small added file should be included fully
expect(result.fullDiffs.length).toEqual(1);
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('src/new-file.ts');
// Verify the file appears in the output
expect(formatted).toInclude('FULL DIFFS');
});
tap.test('DiffProcessor should handle mixed file sizes', async () => {
const processor = new DiffProcessor();
const diffs = [
createSmallDiff('src/small.ts'),
createMediumDiff('src/medium.ts'),
createLargeDiff('dist/large.js'),
];
const result = processor.processDiffs(diffs);
expect(result.totalFiles).toEqual(3);
expect(result.fullDiffs.length).toEqual(1); // small file
expect(result.summarizedDiffs.length).toEqual(1); // medium file
expect(result.metadataOnly.length).toEqual(1); // large file
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('FULL DIFFS (1 files)');
expect(formatted).toInclude('SUMMARIZED DIFFS (1 files)');
expect(formatted).toInclude('METADATA ONLY (1 files)');
});
tap.test('DiffProcessor should handle empty diff array', async () => {
const processor = new DiffProcessor();
const result = processor.processDiffs([]);
expect(result.totalFiles).toEqual(0);
expect(result.fullDiffs.length).toEqual(0);
expect(result.summarizedDiffs.length).toEqual(0);
expect(result.metadataOnly.length).toEqual(0);
expect(result.totalTokens).toEqual(0);
});
tap.test('DiffProcessor should generate comprehensive summary', async () => {
const processor = new DiffProcessor();
const diffs = [
createSmallDiff('src/file1.ts'),
createSmallDiff('src/file2.ts'),
createMediumDiff('src/file3.ts'),
createLargeDiff('dist/bundle.js'),
];
const result = processor.processDiffs(diffs);
const formatted = processor.formatForContext(result);
expect(formatted).toInclude('GIT DIFF SUMMARY');
expect(formatted).toInclude('Files changed: 4 total');
expect(formatted).toInclude('included in full');
expect(formatted).toInclude('summarized');
expect(formatted).toInclude('metadata only');
expect(formatted).toInclude('Estimated tokens:');
expect(formatted).toInclude('END OF GIT DIFF');
});
tap.test('DiffProcessor should handle custom options', async () => {
const processor = new DiffProcessor({
maxDiffTokens: 50000,
smallFileLines: 30,
mediumFileLines: 150,
sampleHeadLines: 10,
sampleTailLines: 10,
});
const mediumDiff = createMediumDiff('src/file.ts'); // 150 lines
const result = processor.processDiffs([mediumDiff]);
// With custom settings, this should be summarized (exactly at the mediumFileLines threshold)
expect(result.summarizedDiffs.length).toEqual(1);
});
tap.test('DiffProcessor should prioritize test files appropriately', async () => {
const processor = new DiffProcessor();
const diffs = [
createSmallDiff('src/core.ts'),
createSmallDiff('test/core.test.ts'),
createSmallDiff('config.json'),
];
const result = processor.processDiffs(diffs);
const formatted = processor.formatForContext(result);
// Source files should come before test files
const srcIndex = formatted.indexOf('src/core.ts');
const testIndex = formatted.indexOf('test/core.test.ts');
expect(srcIndex).toBeLessThan(testIndex);
});
tap.test('DiffProcessor should handle files with no changes gracefully', async () => {
const processor = new DiffProcessor();
const emptyDiff = `--- a/src/file.ts
+++ b/src/file.ts
@@ -1,1 +1,1 @@`;
const result = processor.processDiffs([emptyDiff]);
expect(result.totalFiles).toEqual(1);
expect(result.fullDiffs.length).toEqual(1); // Still included as a small file
});
export default tap.start();

View File

@@ -1,8 +0,0 @@
import { expect, tap } from '@push.rocks/tapbundle';
import * as tsdoc from '../ts/index.js';
tap.test('first test', async () => {
console.log('test');
});
tap.start();

View File

@@ -1,8 +1,8 @@
/** /**
* autocreated commitinfo by @pushrocks/commitinfo * autocreated commitinfo by @push.rocks/commitinfo
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tsdoc', name: '@git.zone/tsdoc',
version: '1.1.25', version: '1.11.0',
description: 'An advanced TypeScript documentation tool leveraging AI for enhanced insights and automated documentation generation, with capabilities for automated and enhanced documentation creation tailor-made for TypeScript projects.' description: 'A comprehensive TypeScript documentation tool that leverages AI to generate and enhance project documentation, including dynamic README creation, API docs via TypeDoc, and smart commit message generation.'
} }

302
ts/aidocs_classes/commit.ts Normal file
View File

@@ -0,0 +1,302 @@
import * as plugins from '../plugins.js';
import { AiDoc } from '../classes.aidoc.js';
import { ProjectContext } from './projectcontext.js';
import { DiffProcessor } from '../classes.diffprocessor.js';
import { logger } from '../logging.js';
export interface INextCommitObject {
recommendedNextVersionLevel: 'fix' | 'feat' | 'BREAKING CHANGE'; // the recommended next version level of the project
recommendedNextVersionScope: string; // the recommended scope name of the next version, like "core" or "cli", or specific class names.
recommendedNextVersionMessage: string; // the commit message. Don't put fix() feat() or BREAKING CHANGE in the message. Please just the message itself.
recommendedNextVersionDetails: string[]; // detailed bullet points for the changelog
recommendedNextVersion: string; // the recommended next version of the project, x.x.x
changelog?: string; // the changelog for the next version
}
export class Commit {
private aiDocsRef: AiDoc;
private projectDir: string;
constructor(aiDocsRef: AiDoc, projectDirArg: string) {
this.aiDocsRef = aiDocsRef;
this.projectDir = projectDirArg;
}
public async buildNextCommitObject(): Promise<INextCommitObject> {
const smartgitInstance = new plugins.smartgit.Smartgit();
await smartgitInstance.init();
const gitRepo = await plugins.smartgit.GitRepo.fromOpeningRepoDir(
smartgitInstance,
this.projectDir
);
// Define comprehensive exclusion patterns
// smartgit@3.3.0+ supports glob patterns natively
const excludePatterns = [
// Lock files
'pnpm-lock.yaml',
'package-lock.json',
'npm-shrinkwrap.json',
'yarn.lock',
'deno.lock',
'bun.lockb',
// Build artifacts (main culprit for large diffs!)
'dist/**',
'dist_*/**', // dist_ts, dist_web, etc.
'build/**',
'.next/**',
'out/**',
'public/dist/**',
// Compiled/bundled files
'**/*.js.map',
'**/*.d.ts.map',
'**/*.min.js',
'**/*.bundle.js',
'**/*.chunk.js',
// IDE/Editor directories
'.claude/**',
'.cursor/**',
'.vscode/**',
'.idea/**',
'**/*.swp',
'**/*.swo',
// Logs and caches
'.nogit/**',
'**/*.log',
'.cache/**',
'.rpt2_cache/**',
'coverage/**',
'.nyc_output/**',
];
// Pass glob patterns directly to smartgit - it handles matching internally
const diffStringArray = await gitRepo.getUncommittedDiff(excludePatterns);
// Process diffs intelligently using DiffProcessor
let processedDiffString: string;
if (diffStringArray.length > 0) {
// Diagnostic logging for raw diff statistics
const totalChars = diffStringArray.join('\n\n').length;
const estimatedTokens = Math.ceil(totalChars / 4);
console.log(`📊 Raw git diff statistics:`);
console.log(` Files changed: ${diffStringArray.length}`);
console.log(` Total characters: ${totalChars.toLocaleString()}`);
console.log(` Estimated tokens: ${estimatedTokens.toLocaleString()}`);
console.log(` Exclusion patterns: ${excludePatterns.length}`);
// Use DiffProcessor to intelligently handle large diffs
const diffProcessor = new DiffProcessor({
maxDiffTokens: 100000, // Reserve 100k tokens for diffs
smallFileLines: 300, // Most source files are under 300 lines
mediumFileLines: 800, // Only very large files get head/tail treatment
sampleHeadLines: 75, // When sampling, show more context
sampleTailLines: 75, // When sampling, show more context
});
const processedDiff = diffProcessor.processDiffs(diffStringArray);
processedDiffString = diffProcessor.formatForContext(processedDiff);
console.log(`📝 Processed diff statistics:`);
console.log(` Full diffs: ${processedDiff.fullDiffs.length} files`);
console.log(` Summarized: ${processedDiff.summarizedDiffs.length} files`);
console.log(` Metadata only: ${processedDiff.metadataOnly.length} files`);
console.log(` Final tokens: ${processedDiff.totalTokens.toLocaleString()}`);
if (estimatedTokens > 50000) {
console.log(`✅ DiffProcessor reduced token usage: ${estimatedTokens.toLocaleString()}${processedDiff.totalTokens.toLocaleString()}`);
}
} else {
processedDiffString = 'No changes.';
}
// Use DualAgentOrchestrator for commit message generation
const commitOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
smartAiInstance: this.aiDocsRef.smartAiInstance,
defaultProvider: 'openai',
logPrefix: '[Commit]',
onProgress: (event) => logger.log(event.logLevel, event.logMessage),
guardianPolicyPrompt: `
You validate commit messages for semantic versioning compliance.
APPROVE tool calls for:
- Reading package.json or source files to understand project context
- Using tree to see project structure
- Listing directory contents
REJECT tool calls for:
- Reading files outside the project directory
- Writing, deleting, or modifying any files
- Any destructive operations
APPROVE final output if:
- Version level (fix/feat/BREAKING CHANGE) matches the scope of changes in the diff
- Commit message is clear, professional, and follows conventional commit conventions
- No personal information, licensing details, or AI mentions (Claude/Codex) included
- JSON structure is valid with all required fields
- Scope accurately reflects the changed modules/files
REJECT final output if:
- Version level doesn't match the scope of changes (e.g., "feat" for a typo fix should be "fix")
- Message is vague, unprofessional, or contains sensitive information
- JSON is malformed or missing required fields
`,
});
// Register scoped filesystem tool for agent exploration
commitOrchestrator.registerScopedFilesystemTool(this.projectDir, [
'.nogit/**',
'node_modules/**',
'.git/**',
'dist/**',
'dist_*/**',
]);
await commitOrchestrator.start();
const commitTaskPrompt = `
You create a commit message for a git commit.
Project directory: ${this.projectDir}
You have access to a filesystem tool to explore the project if needed:
- Use tree to see project structure
- Use read to read package.json or source files for context
Analyze the git diff below to understand what changed and generate a commit message.
You should not include any licensing information or personal information.
Never mention CLAUDE code, or codex.
Your final output (inside the task_complete tags) must be ONLY valid JSON - the raw JSON object, nothing else.
No explanations, no summaries, no markdown - just the JSON object that can be parsed with JSON.parse().
Here is the structure of the JSON you must return:
{
"recommendedNextVersionLevel": "fix" | "feat" | "BREAKING CHANGE",
"recommendedNextVersionScope": "string",
"recommendedNextVersionMessage": "string",
"recommendedNextVersionDetails": ["string"],
"recommendedNextVersion": "x.x.x"
}
For recommendedNextVersionDetails, only add entries that have obvious value to the reader.
Here is the git diff showing what changed:
${processedDiffString}
Analyze these changes and output the JSON commit message object.
`;
const commitResult = await commitOrchestrator.run(commitTaskPrompt);
await commitOrchestrator.stop();
if (!commitResult.success) {
throw new Error(`Commit message generation failed: ${commitResult.status}`);
}
// Extract JSON from result - handle cases where AI adds text around it
let jsonString = commitResult.result
.replace(/```json\n?/gi, '')
.replace(/```\n?/gi, '');
// Try to find JSON object in the result
const jsonMatch = jsonString.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
throw new Error(`Could not find JSON object in result: ${jsonString.substring(0, 100)}...`);
}
jsonString = jsonMatch[0];
const resultObject: INextCommitObject = JSON.parse(jsonString);
const previousChangelogPath = plugins.path.join(this.projectDir, 'changelog.md');
let previousChangelog: plugins.smartfile.SmartFile;
if (await plugins.fsInstance.file(previousChangelogPath).exists()) {
previousChangelog = await plugins.smartfileFactory.fromFilePath(previousChangelogPath);
}
if (!previousChangelog) {
// lets build the changelog based on that
const commitMessages = await gitRepo.getAllCommitMessages();
console.log(JSON.stringify(commitMessages, null, 2));
// Use DualAgentOrchestrator for changelog generation with Guardian validation
const changelogOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
smartAiInstance: this.aiDocsRef.smartAiInstance,
defaultProvider: 'openai',
logPrefix: '[Changelog]',
onProgress: (event) => logger.log(event.logLevel, event.logMessage),
guardianPolicyPrompt: `
You validate changelog generation.
APPROVE if:
- Changelog follows proper markdown format with ## headers for each version
- Entries are chronologically ordered (newest first)
- Version ranges for trivial commits are properly summarized
- No duplicate or empty entries
- Format matches: ## yyyy-mm-dd - x.x.x - scope
REJECT with feedback if:
- Markdown formatting is incorrect
- Entries are not meaningful or helpful
- Dates or versions are malformed
`,
});
await changelogOrchestrator.start();
const changelogTaskPrompt = `
You are building a changelog.md file for the project.
Omit commits and versions that lack relevant changes, but make sure to mention them as a range with a summarizing message instead.
A changelog entry should look like this:
## yyyy-mm-dd - x.x.x - scope here
main descriptiom here
- detailed bullet points follow
You are given:
* the commit messages of the project
Only return the changelog file content, so it can be written directly to changelog.md.
Here are the commit messages:
${JSON.stringify(commitMessages, null, 2)}
`;
const changelogResult = await changelogOrchestrator.run(changelogTaskPrompt);
await changelogOrchestrator.stop();
if (!changelogResult.success) {
throw new Error(`Changelog generation failed: ${changelogResult.status}`);
}
previousChangelog = plugins.smartfileFactory.fromString(
previousChangelogPath,
changelogResult.result.replaceAll('```markdown', '').replaceAll('```', ''),
'utf8'
);
}
let oldChangelog = previousChangelog.contents.toString().replace('# Changelog\n\n', '');
if (oldChangelog.startsWith('\n')) {
oldChangelog = oldChangelog.replace('\n', '');
}
let newDateString = new plugins.smarttime.ExtendedDate().exportToHyphedSortableDate();
let newChangelog = `# Changelog\n\n${`## ${newDateString} - {{nextVersion}} - {{nextVersionScope}}
{{nextVersionMessage}}
{{nextVersionDetails}}`}\n\n${oldChangelog}`;
resultObject.changelog = newChangelog;
return resultObject;
}
}

View File

@@ -1,6 +1,7 @@
import type { AiDoc } from '../classes.aidoc.js'; import type { AiDoc } from '../classes.aidoc.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { ProjectContext } from './projectcontext.js'; import { ProjectContext } from './projectcontext.js';
import { logger } from '../logging.js';
interface IDescriptionInterface { interface IDescriptionInterface {
description: string; description: string;
@@ -8,7 +9,6 @@ interface IDescriptionInterface {
} }
export class Description { export class Description {
// INSTANCE // INSTANCE
private aiDocsRef: AiDoc; private aiDocsRef: AiDoc;
private projectDir: string; private projectDir: string;
@@ -19,55 +19,107 @@ export class Description {
} }
public async build() { public async build() {
// we can now assemble the directory structure. // Use DualAgentOrchestrator with filesystem tool for agent-driven exploration
const projectContext = new ProjectContext(this.projectDir); const descriptionOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
const contextString = await projectContext.update(); smartAiInstance: this.aiDocsRef.smartAiInstance,
defaultProvider: 'openai',
maxIterations: 15,
maxResultChars: 10000, // Limit tool output to prevent token explosion
maxHistoryMessages: 15, // Limit history window
logPrefix: '[Description]',
onProgress: (event) => logger.log(event.logLevel, event.logMessage),
guardianPolicyPrompt: `
You validate description generation tool calls and outputs.
let result = await this.aiDocsRef.openaiInstance.chat( APPROVE tool calls for:
` - Reading package.json, npmextra.json, or source files in the ts/ directory
You create a json adhering the following interface: - Listing directory contents to understand project structure
- Using tree to see project structure
REJECT tool calls for:
- Reading files outside the project directory
- Writing, deleting, or modifying any files
- Any destructive operations
For final output, APPROVE if:
- JSON is valid and parseable
- Description is a clear, concise one-sentence summary
- Keywords are relevant to the project's use cases
- Both description and keywords fields are present
REJECT final output if:
- JSON is malformed or wrapped in markdown code blocks
- Description is too long or vague
- Keywords are irrelevant or generic
`,
});
// Register scoped filesystem tool for agent exploration
descriptionOrchestrator.registerScopedFilesystemTool(this.projectDir);
await descriptionOrchestrator.start();
const descriptionTaskPrompt = `
You create a project description and keywords for an npm package.
PROJECT DIRECTORY: ${this.projectDir}
Use the filesystem tool to explore the project and understand what it does:
1. First, use tree to see the project structure
2. Read package.json to understand the package name and current description
3. Read npmextra.json if it exists for additional metadata
4. Read key source files in ts/ directory to understand the implementation
Then generate a description and keywords based on your exploration.
Your FINAL response must be valid JSON adhering to this interface:
{ {
description: string; // a sensible short, one sentence description of the project description: string; // a sensible short, one sentence description of the project
keywords: string[]; // an array of tags that describe the project keywords: string[]; // an array of tags that describe the project based on use cases
} }
The description should be based on what you understand from the project's files.
The keywords should be based on use cases you see from the files.
Don't be cheap about the way you think.
Important: Answer only in valid JSON. Important: Answer only in valid JSON.
You answer should be parseable with JSON.parse() without modifying anything. Your answer should be parseable with JSON.parse() without modifying anything.
Don't wrap the JSON in \`\`\`json\`\`\` - just return the raw JSON object.
`;
Don't wrap the JSON in three ticks json!!! const descriptionResult = await descriptionOrchestrator.run(descriptionTaskPrompt);
`, await descriptionOrchestrator.stop();
contextString,
[] if (!descriptionResult.success) {
throw new Error(`Description generation failed: ${descriptionResult.status}`);
}
console.log(descriptionResult.result);
const resultObject: IDescriptionInterface = JSON.parse(
descriptionResult.result.replace('```json', '').replace('```', ''),
); );
console.log(result.message.content); // Use ProjectContext to get file handles for writing
const resultObject: IDescriptionInterface = JSON.parse(result.message.content.replace('```json', '').replace('```', '')); const projectContext = new ProjectContext(this.projectDir);
const files = await projectContext.gatherFiles();
const npmextraJson = (await projectContext.gatherFiles()).smartfilesNpmextraJSON; // Update npmextra.json
const npmextraJson = files.smartfilesNpmextraJSON;
const npmextraJsonContent = JSON.parse(npmextraJson.contents.toString()); const npmextraJsonContent = JSON.parse(npmextraJson.contents.toString());
npmextraJsonContent.gitzone.module.description = resultObject.description; npmextraJsonContent['@git.zone/cli'].module.description = resultObject.description;
npmextraJsonContent.gitzone.module.keywords = resultObject.keywords; npmextraJsonContent['@git.zone/cli'].module.keywords = resultObject.keywords;
npmextraJson.contents = Buffer.from(JSON.stringify(npmextraJsonContent, null, 2)); npmextraJson.contents = Buffer.from(JSON.stringify(npmextraJsonContent, null, 2));
await npmextraJson.write(); await npmextraJson.write();
// do the same with packageJson // Update package.json
const packageJson = (await projectContext.gatherFiles()).smartfilePackageJSON; const packageJson = files.smartfilePackageJSON;
const packageJsonContent = JSON.parse(packageJson.contents.toString()); const packageJsonContent = JSON.parse(packageJson.contents.toString());
packageJsonContent.description = resultObject.description; packageJsonContent.description = resultObject.description;
packageJsonContent.keywords = resultObject.keywords; packageJsonContent.keywords = resultObject.keywords;
packageJson.contents = Buffer.from(JSON.stringify(packageJsonContent, null, 2)); packageJson.contents = Buffer.from(JSON.stringify(packageJsonContent, null, 2));
await packageJson.write(); await packageJson.write();
console.log(`\n======================\n`); console.log(`\n======================\n`);
console.log(JSON.stringify(resultObject, null, 2)); console.log(JSON.stringify(resultObject, null, 2));
console.log(`\n======================\n`); console.log(`\n======================\n`);
return result.message.content; return descriptionResult.result;
} }
} }

View File

@@ -1,3 +1,4 @@
export * from './commit.js';
export * from './description.js'; export * from './description.js';
export * from './projectcontext.js'; export * from './projectcontext.js';
export * from './readme.js'; export * from './readme.js';

View File

@@ -5,37 +5,37 @@ export class ProjectContext {
// INSTANCE // INSTANCE
public projectDir: string; public projectDir: string;
private tokenCount: number = 0;
private contextString: string = '';
constructor(projectDirArg: string) { constructor(projectDirArg: string) {
this.projectDir = projectDirArg; this.projectDir = projectDirArg;
} }
public async gatherFiles() { public async gatherFiles() {
const smartfilePackageJSON = await plugins.smartfile.SmartFile.fromFilePath( const smartfilePackageJSON = await plugins.smartfileFactory.fromFilePath(
plugins.path.join(this.projectDir, 'package.json'), plugins.path.join(this.projectDir, 'package.json'),
this.projectDir this.projectDir,
); );
const smartfilesReadme = await plugins.smartfile.SmartFile.fromFilePath( const smartfilesReadme = await plugins.smartfileFactory.fromFilePath(
plugins.path.join(this.projectDir, 'readme.md'), plugins.path.join(this.projectDir, 'readme.md'),
this.projectDir this.projectDir,
); );
const smartfilesReadmeHints = await plugins.smartfile.SmartFile.fromFilePath( const smartfilesReadmeHints = await plugins.smartfileFactory.fromFilePath(
plugins.path.join(this.projectDir, 'readme.hints.md'), plugins.path.join(this.projectDir, 'readme.hints.md'),
this.projectDir this.projectDir,
); );
const smartfilesNpmextraJSON = await plugins.smartfile.SmartFile.fromFilePath( const smartfilesNpmextraJSON = await plugins.smartfileFactory.fromFilePath(
plugins.path.join(this.projectDir, 'npmextra.json'), plugins.path.join(this.projectDir, 'npmextra.json'),
this.projectDir
);
const smartfilesMod = await plugins.smartfile.fs.fileTreeToObject(
this.projectDir, this.projectDir,
'ts/**/*.ts'
); );
const smartfilesTest = await plugins.smartfile.fs.fileTreeToObject( const smartfilesMod = await plugins.smartfileFactory.virtualDirectoryFromPath(
this.projectDir, this.projectDir,
'test/**/*.ts' ).then(vd => vd.filter(f => f.relative.startsWith('ts') && f.relative.endsWith('.ts')).listFiles());
); const smartfilesTest = await plugins.smartfileFactory.virtualDirectoryFromPath(
this.projectDir,
).then(vd => vd.filter(f => f.relative.startsWith('test/') && f.relative.endsWith('.ts')).listFiles());
return { return {
smartfilePackageJSON, smartfilePackageJSON,
smartfilesReadme, smartfilesReadme,
@@ -47,6 +47,9 @@ export class ProjectContext {
} }
public async convertFilesToContext(filesArg: plugins.smartfile.SmartFile[]) { public async convertFilesToContext(filesArg: plugins.smartfile.SmartFile[]) {
filesArg.map((fileArg) => {
// console.log(` -> ${fileArg.relative}`);
});
return filesArg return filesArg
.map((smartfile) => { .map((smartfile) => {
return ` return `
@@ -60,6 +63,17 @@ ${smartfile.contents.toString()}
.join('\n'); .join('\n');
} }
/**
* Estimate token count for a string
* Uses a rough estimate of 4 characters per token
* @param text The text to estimate tokens for
* @returns Estimated number of tokens
*/
public countTokens(text: string): number {
// Rough estimate: ~4 characters per token for English text
return Math.ceil(text.length / 4);
}
private async buildContext(dirArg: string) { private async buildContext(dirArg: string) {
const files = await this.gatherFiles(); const files = await this.gatherFiles();
let context = await this.convertFilesToContext([ let context = await this.convertFilesToContext([
@@ -70,9 +84,33 @@ ${smartfile.contents.toString()}
...files.smartfilesMod, ...files.smartfilesMod,
...files.smartfilesTest, ...files.smartfilesTest,
]); ]);
// Count tokens in the context
this.contextString = context;
this.tokenCount = this.countTokens(context);
// console.log(context);
return context; return context;
} }
/**
* Get the token count for the current context
* @returns The number of tokens in the context
*/
public getTokenCount(): number {
return this.tokenCount;
}
/**
* Get both the context string and its token count
* @returns An object containing the context string and token count
*/
public getContextWithTokenCount(): { context: string; tokenCount: number } {
return {
context: this.contextString,
tokenCount: this.tokenCount
};
}
public async update() { public async update() {
const result = await this.buildContext(this.projectDir); const result = await this.buildContext(this.projectDir);
return result; return result;

View File

@@ -1,9 +1,10 @@
import type { AiDoc } from '../classes.aidoc.js'; import type { AiDoc } from '../classes.aidoc.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { ProjectContext } from './projectcontext.js'; import { ProjectContext } from './projectcontext.js';
import { logger } from '../logging.js';
export class Readme { export class Readme {
// INSTANCE // INSTANCE
private aiDocsRef: AiDoc; private aiDocsRef: AiDoc;
private projectDir: string; private projectDir: string;
@@ -16,34 +17,79 @@ export class Readme {
public async build() { public async build() {
let finalReadmeString = ``; let finalReadmeString = ``;
// we can now assemble the directory structure. // First check legal info before introducing any cost
const projectContext = new ProjectContext(this.projectDir); const projectContext = new ProjectContext(this.projectDir);
const contextString = await projectContext.update(); const npmExtraJson = JSON.parse(
(await projectContext.gatherFiles()).smartfilesNpmextraJSON.contents.toString()
// lets first check legal before introducung any cost );
const npmExtraJson = JSON.parse(((await projectContext.gatherFiles()).smartfilesNpmextraJSON).contents.toString()); const legalInfo = npmExtraJson?.['@git.zone/tsdoc']?.legal;
const legalInfo = npmExtraJson?.tsdoc?.legal
if (!legalInfo) { if (!legalInfo) {
const error = new Error(`No legal information found in npmextra.json`); const error = new Error(`No legal information found in npmextra.json`);
console.log(error); console.log(error);
} }
let result = await this.aiDocsRef.openaiInstance.chat( // Use DualAgentOrchestrator with filesystem tool for agent-driven exploration
` const readmeOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
You create markdown readmes for npm projects. You only output the markdown readme. smartAiInstance: this.aiDocsRef.smartAiInstance,
defaultProvider: 'openai',
maxIterations: 25,
maxResultChars: 15000, // Limit tool output to prevent token explosion
maxHistoryMessages: 20, // Limit history window
logPrefix: '[README]',
onProgress: (event) => logger.log(event.logLevel, event.logMessage),
guardianPolicyPrompt: `
You validate README generation tool calls and outputs.
The Readme should follow the following template: APPROVE tool calls for:
- Reading any files within the project directory (package.json, ts/*.ts, readme.md, etc.)
- Using tree to see project structure
- Using glob to find source files
- Listing directory contents
REJECT tool calls for:
- Reading files outside the project directory
- Writing, deleting, or modifying any files
- Any destructive operations
For final README output, APPROVE if:
- README follows proper markdown format
- Contains Install and Usage sections
- Code examples are correct TypeScript/ESM syntax
- Documentation is comprehensive and helpful
REJECT final output if:
- README is incomplete or poorly formatted
- Contains licensing information (added separately)
- Uses CommonJS syntax instead of ESM
- Contains "in conclusion" or similar filler
`,
});
// Register scoped filesystem tool for agent exploration
readmeOrchestrator.registerScopedFilesystemTool(this.projectDir);
await readmeOrchestrator.start();
const readmeTaskPrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.
PROJECT DIRECTORY: ${this.projectDir}
Use the filesystem tool to explore the project and understand what it does:
1. First, use tree to see the project structure (maxDepth: 3)
2. Read package.json to understand the package name, description, and dependencies
3. Read the existing readme.md if it exists (use it as a base, improve and expand)
4. Read readme.hints.md if it exists (contains hints for documentation)
5. Read key source files in ts/ directory to understand the API and implementation
6. Focus on exported classes, interfaces, and functions
Then generate a comprehensive README following this template:
# Project Name # Project Name
[ [The name from package.json and description]
The name is the module name of package.json
The description is in the description field of package.json
]
## Install ## Install
[ [Short text on how to install the project]
Write a short text on how to install the project
]
## Usage ## Usage
[ [
@@ -51,36 +97,134 @@ The Readme should follow the following template:
Construct sensible scenarios for the user. Construct sensible scenarios for the user.
Make sure to show a complete set of features of the module. Make sure to show a complete set of features of the module.
Don't omit use cases. Don't omit use cases.
It does not matter how much time you need.
ALWAYS USE ESM SYNTAX AND TYPESCRIPT. ALWAYS USE ESM SYNTAX AND TYPESCRIPT.
DON'T CHICKEN OUT. Write at least 4000 words. More if necessary. Write at least 4000 words. More if necessary.
If there is already a readme, take the Usage section as base. Remove outdated content, and expand and improve upon the valid parts. If there is already a readme, take the Usage section as base. Remove outdated content, expand and improve.
Super important: Check for completenes. Check for completeness.
Don't include any licensing information. This will be added in a later step. Don't include any licensing information. This will be added later.
Avoid "in conclusions". Avoid "in conclusion" statements.
Good to know:
* npmextra.json contains overall module information.
* readme.hints.md provides valuable hints about module ideas.
] ]
`, `;
contextString,
[]
);
const readmeResult = await readmeOrchestrator.run(readmeTaskPrompt);
await readmeOrchestrator.stop();
finalReadmeString += result.message.content + '\n' + legalInfo; if (!readmeResult.success) {
throw new Error(`README generation failed: ${readmeResult.status}`);
}
// Clean up markdown formatting if wrapped in code blocks
let resultMessage = readmeResult.result
.replace(/^```markdown\n?/i, '')
.replace(/\n?```$/i, '');
finalReadmeString += resultMessage + '\n' + legalInfo;
console.log(`\n======================\n`); console.log(`\n======================\n`);
console.log(result.message.content); console.log(resultMessage);
console.log(`\n======================\n`); console.log(`\n======================\n`);
const readme = (await projectContext.gatherFiles()).smartfilesReadme; const readme = (await projectContext.gatherFiles()).smartfilesReadme;
readme.contents = Buffer.from(finalReadmeString); readme.contents = Buffer.from(finalReadmeString);
await readme.write(); await readme.write();
return result.message.content; // lets care about monorepo aspects
const tsPublishInstance = new plugins.tspublish.TsPublish();
const subModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
logger.log('info', `Found ${Object.keys(subModules).length} sub modules`);
for (const subModule of Object.keys(subModules)) {
logger.log('info', `Building readme for ${subModule}`);
const subModulePath = plugins.path.join(paths.cwd, subModule);
const tspublishData = await plugins.fsInstance
.file(plugins.path.join(subModulePath, 'tspublish.json'))
.encoding('utf8')
.read();
// Create a new orchestrator with filesystem tool for each submodule
const subModuleOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
smartAiInstance: this.aiDocsRef.smartAiInstance,
defaultProvider: 'openai',
maxIterations: 20,
maxResultChars: 12000,
maxHistoryMessages: 15,
logPrefix: `[README:${subModule}]`,
onProgress: (event) => logger.log(event.logLevel, event.logMessage),
guardianPolicyPrompt: `
You validate README generation for submodules.
APPROVE tool calls for:
- Reading any files within the submodule directory
- Using tree to see structure
- Using glob to find source files
REJECT tool calls for:
- Reading files outside the submodule directory
- Writing, deleting, or modifying any files
- Any destructive operations
APPROVE final README if comprehensive, well-formatted markdown with ESM TypeScript examples.
REJECT incomplete READMEs or those with licensing info.
`,
});
// Register scoped filesystem tool for the submodule directory
subModuleOrchestrator.registerScopedFilesystemTool(subModulePath);
await subModuleOrchestrator.start();
const subModulePrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.
SUB MODULE: ${subModule}
SUB MODULE DIRECTORY: ${subModulePath}
IMPORTANT: YOU ARE CREATING THE README FOR THIS SUB MODULE: ${subModule}
The Sub Module will be published with:
${JSON.stringify(tspublishData, null, 2)}
Use the filesystem tool to explore the submodule:
1. Use tree to see the submodule structure
2. Read package.json to understand the submodule
3. Read source files in ts/ directory to understand the implementation
Generate a README following the template:
# Project Name
[name and description from package.json]
## Install
[installation instructions]
## Usage
[
Code examples with complete features.
ESM TypeScript syntax only.
Write at least 4000 words.
No licensing information.
No "in conclusion".
]
Don't use \`\`\` at the beginning or end. Only for code blocks.
`;
const subModuleResult = await subModuleOrchestrator.run(subModulePrompt);
await subModuleOrchestrator.stop();
if (subModuleResult.success) {
const subModuleReadmeString = subModuleResult.result
.replace(/^```markdown\n?/i, '')
.replace(/\n?```$/i, '') + '\n' + legalInfo;
await plugins.fsInstance
.file(plugins.path.join(subModulePath, 'readme.md'))
.encoding('utf8')
.write(subModuleReadmeString);
logger.log('success', `Built readme for ${subModule}`);
} else {
logger.log('error', `Failed to build readme for ${subModule}: ${subModuleResult.status}`);
}
}
return resultMessage;
} }
} }

View File

@@ -7,8 +7,8 @@ export class AiDoc {
public npmextraKV: plugins.npmextra.KeyValueStore; public npmextraKV: plugins.npmextra.KeyValueStore;
public qenvInstance: plugins.qenv.Qenv; public qenvInstance: plugins.qenv.Qenv;
public smartinteractInstance: plugins.smartinteract.SmartInteract; public aidocInteract: plugins.smartinteract.SmartInteract;
public openaiInstance: plugins.smartai.OpenAiProvider; public smartAiInstance: plugins.smartai.SmartAi;
argvArg: any; argvArg: any;
@@ -33,12 +33,28 @@ export class AiDoc {
public async start() { public async start() {
// lets care about prerequisites // lets care about prerequisites
this.smartinteractInstance = new plugins.smartinteract.SmartInteract(); this.aidocInteract = new plugins.smartinteract.SmartInteract();
this.qenvInstance = new plugins.qenv.Qenv(); this.qenvInstance = new plugins.qenv.Qenv();
if (!(await this.qenvInstance.getEnvVarOnDemand('OPENAI_TOKEN'))) { if (!(await this.qenvInstance.getEnvVarOnDemand('OPENAI_TOKEN'))) {
// Migrate old KV store path to new path if needed
const homeDir = plugins.smartpath.get.home();
const oldKvPath = plugins.path.join(homeDir, '.npmextra/kv/tsdoc.json');
const newKvDir = plugins.path.join(homeDir, '.npmextra/kv/@git.zone');
const newKvPath = plugins.path.join(newKvDir, 'tsdoc.json');
if (
await plugins.fsInstance.file(oldKvPath).exists() &&
!(await plugins.fsInstance.file(newKvPath).exists())
) {
console.log('Migrating tsdoc KeyValueStore to @git.zone/tsdoc...');
await plugins.fsInstance.directory(newKvDir).recursive().create();
await plugins.fsInstance.file(oldKvPath).copy(newKvPath);
await plugins.fsInstance.file(oldKvPath).delete();
console.log('Migration complete: tsdoc.json -> @git.zone/tsdoc.json');
}
this.npmextraKV = new plugins.npmextra.KeyValueStore({ this.npmextraKV = new plugins.npmextra.KeyValueStore({
typeArg: 'userHomeDir', typeArg: 'userHomeDir',
identityArg: 'tsdoc', identityArg: '@git.zone/tsdoc',
mandatoryKeys: ['OPENAI_TOKEN'], mandatoryKeys: ['OPENAI_TOKEN'],
}); });
@@ -51,9 +67,9 @@ export class AiDoc {
// lets try smartinteract // lets try smartinteract
// wait for a second until OpenAI fixes punycode problem... // wait for a second until OpenAI fixes punycode problem...
await plugins.smartdelay.delayFor(1000); await plugins.smartdelay.delayFor(1000);
const answerObject = await this.smartinteractInstance.askQuestion({ const answerObject = await this.aidocInteract.askQuestion({
type: 'input', type: 'input',
message: `Please provide your OpenAI token`, message: `Please provide your OpenAI token. This will be persisted in your home directory.`,
name: 'OPENAI_TOKEN', name: 'OPENAI_TOKEN',
default: '', default: '',
}); });
@@ -64,10 +80,35 @@ export class AiDoc {
await this.npmextraKV.writeKey('OPENAI_TOKEN', this.openaiToken); await this.npmextraKV.writeKey('OPENAI_TOKEN', this.openaiToken);
} }
} }
if (!this.openaiToken && this.npmextraKV) {
this.openaiToken = await this.npmextraKV.readKey('OPENAI_TOKEN');
}
// lets assume we have an OPENAI_Token now // lets assume we have an OPENAI_Token now
this.openaiInstance = new plugins.smartai.OpenAiProvider(this.openaiToken); this.smartAiInstance = new plugins.smartai.SmartAi({
await this.openaiInstance.start(); openaiToken: this.openaiToken,
});
await this.smartAiInstance.start();
}
public async stop() {
if (this.smartAiInstance) {
await this.smartAiInstance.stop();
}
// No explicit cleanup needed for npmextraKV or aidocInteract
// They don't keep event loop alive
}
/**
* Get the OpenAI provider for direct chat calls
* This is a convenience getter to access the provider from SmartAi
*/
public get openaiProvider(): plugins.smartai.OpenAiProvider {
return this.smartAiInstance.openaiProvider;
}
public getOpenaiToken(): string {
return this.openaiToken;
} }
public async buildReadme(projectDirArg: string) { public async buildReadme(projectDirArg: string) {
@@ -79,4 +120,46 @@ export class AiDoc {
const descriptionInstance = new aiDocsClasses.Description(this, projectDirArg); const descriptionInstance = new aiDocsClasses.Description(this, projectDirArg);
return await descriptionInstance.build(); return await descriptionInstance.build();
} }
public async buildNextCommitObject(projectDirArg: string) {
const commitInstance = new aiDocsClasses.Commit(this, projectDirArg);
return await commitInstance.buildNextCommitObject();
}
public async getProjectContext(projectDirArg: string) {
const projectContextInstance = new aiDocsClasses.ProjectContext(projectDirArg);
return await projectContextInstance.gatherFiles();
}
/**
* Get the context with token count information
* @param projectDirArg The path to the project directory
* @returns An object containing the context string and its token count
*/
public async getProjectContextWithTokenCount(projectDirArg: string) {
const projectContextInstance = new aiDocsClasses.ProjectContext(projectDirArg);
await projectContextInstance.update();
return projectContextInstance.getContextWithTokenCount();
}
/**
* Get just the token count for a project's context
* @param projectDirArg The path to the project directory
* @returns The number of tokens in the project context
*/
public async getProjectContextTokenCount(projectDirArg: string) {
const projectContextInstance = new aiDocsClasses.ProjectContext(projectDirArg);
await projectContextInstance.update();
return projectContextInstance.getTokenCount();
}
/**
* Estimate token count in a text string
* @param text The text to estimate tokens for
* @returns Estimated number of tokens
*/
public countTokens(text: string): number {
const projectContextInstance = new aiDocsClasses.ProjectContext('');
return projectContextInstance.countTokens(text);
}
} }

353
ts/classes.diffprocessor.ts Normal file
View File

@@ -0,0 +1,353 @@
/**
* Intelligent git diff processor that handles large diffs by sampling and prioritization
* instead of blind truncation.
*/
export interface IDiffFileInfo {
filepath: string;
status: 'added' | 'modified' | 'deleted';
linesAdded: number;
linesRemoved: number;
totalLines: number;
estimatedTokens: number;
diffContent: string;
}
export interface IProcessedDiff {
summary: string; // Human-readable overview
fullDiffs: string[]; // Small files included fully
summarizedDiffs: string[]; // Medium files with head/tail
metadataOnly: string[]; // Large files, just stats
totalFiles: number;
totalTokens: number;
}
export interface IDiffProcessorOptions {
maxDiffTokens?: number; // Maximum tokens for entire diff section (default: 100000)
smallFileLines?: number; // Files <= this are included fully (default: 50)
mediumFileLines?: number; // Files <= this are summarized (default: 200)
sampleHeadLines?: number; // Lines to show at start of medium files (default: 20)
sampleTailLines?: number; // Lines to show at end of medium files (default: 20)
}
export class DiffProcessor {
private options: Required<IDiffProcessorOptions>;
constructor(options: IDiffProcessorOptions = {}) {
this.options = {
maxDiffTokens: options.maxDiffTokens ?? 100000,
smallFileLines: options.smallFileLines ?? 50,
mediumFileLines: options.mediumFileLines ?? 200,
sampleHeadLines: options.sampleHeadLines ?? 20,
sampleTailLines: options.sampleTailLines ?? 20,
};
}
/**
* Process an array of git diffs into a structured, token-efficient format
*/
public processDiffs(diffStringArray: string[]): IProcessedDiff {
// Parse all diffs into file info objects
const fileInfos: IDiffFileInfo[] = diffStringArray
.map(diffString => this.parseDiffFile(diffString))
.filter(info => info !== null) as IDiffFileInfo[];
// Prioritize files (source files first, build artifacts last)
const prioritized = this.prioritizeFiles(fileInfos);
const result: IProcessedDiff = {
summary: '',
fullDiffs: [],
summarizedDiffs: [],
metadataOnly: [],
totalFiles: prioritized.length,
totalTokens: 0,
};
let tokensUsed = 0;
const tokenBudget = this.options.maxDiffTokens;
// Categorize and include files based on size and token budget
for (const fileInfo of prioritized) {
const remainingBudget = tokenBudget - tokensUsed;
if (remainingBudget <= 0) {
// Budget exhausted - rest are metadata only
result.metadataOnly.push(this.formatMetadataOnly(fileInfo));
continue;
}
if (fileInfo.totalLines <= this.options.smallFileLines) {
// Small file - include fully if budget allows
if (fileInfo.estimatedTokens <= remainingBudget) {
const statusPrefix = this.getFileStatusPrefix(fileInfo);
result.fullDiffs.push(`${statusPrefix}${fileInfo.diffContent}`);
tokensUsed += fileInfo.estimatedTokens;
} else {
result.metadataOnly.push(this.formatMetadataOnly(fileInfo));
}
} else if (fileInfo.totalLines <= this.options.mediumFileLines) {
// Medium file - try to include summary with head/tail
const summary = this.extractDiffSample(
fileInfo,
this.options.sampleHeadLines,
this.options.sampleTailLines
);
const summaryTokens = Math.ceil(summary.length / 4); // Rough estimate
if (summaryTokens <= remainingBudget) {
result.summarizedDiffs.push(summary);
tokensUsed += summaryTokens;
} else {
result.metadataOnly.push(this.formatMetadataOnly(fileInfo));
}
} else {
// Large file - metadata only
result.metadataOnly.push(this.formatMetadataOnly(fileInfo));
}
}
result.totalTokens = tokensUsed;
result.summary = this.generateSummary(result);
return result;
}
/**
* Format the processed diff for inclusion in context
*/
public formatForContext(processed: IProcessedDiff): string {
const sections: string[] = [];
// Summary section
sections.push('====== GIT DIFF SUMMARY ======');
sections.push(processed.summary);
sections.push('');
// Full diffs section
if (processed.fullDiffs.length > 0) {
sections.push(`====== FULL DIFFS (${processed.fullDiffs.length} files) ======`);
sections.push(processed.fullDiffs.join('\n\n'));
sections.push('');
}
// Summarized diffs section
if (processed.summarizedDiffs.length > 0) {
sections.push(`====== SUMMARIZED DIFFS (${processed.summarizedDiffs.length} files) ======`);
sections.push(processed.summarizedDiffs.join('\n\n'));
sections.push('');
}
// Metadata only section
if (processed.metadataOnly.length > 0) {
sections.push(`====== METADATA ONLY (${processed.metadataOnly.length} files) ======`);
sections.push(processed.metadataOnly.join('\n'));
sections.push('');
}
sections.push('====== END OF GIT DIFF ======');
return sections.join('\n');
}
/**
* Parse a single git diff string into file information
*/
private parseDiffFile(diffString: string): IDiffFileInfo | null {
if (!diffString || diffString.trim().length === 0) {
return null;
}
const lines = diffString.split('\n');
let filepath = '';
let status: 'added' | 'modified' | 'deleted' = 'modified';
let linesAdded = 0;
let linesRemoved = 0;
// Parse diff header to extract filepath and status
for (const line of lines) {
if (line.startsWith('--- a/')) {
filepath = line.substring(6);
} else if (line.startsWith('+++ b/')) {
const newPath = line.substring(6);
if (newPath === '/dev/null') {
status = 'deleted';
} else if (filepath === '/dev/null') {
status = 'added';
filepath = newPath;
} else {
filepath = newPath;
}
} else if (line.startsWith('+') && !line.startsWith('+++')) {
linesAdded++;
} else if (line.startsWith('-') && !line.startsWith('---')) {
linesRemoved++;
}
}
const totalLines = linesAdded + linesRemoved;
const estimatedTokens = Math.ceil(diffString.length / 4);
return {
filepath,
status,
linesAdded,
linesRemoved,
totalLines,
estimatedTokens,
diffContent: diffString,
};
}
/**
* Prioritize files by importance (source files before build artifacts)
*/
private prioritizeFiles(files: IDiffFileInfo[]): IDiffFileInfo[] {
return files.sort((a, b) => {
const scoreA = this.getFileImportanceScore(a.filepath);
const scoreB = this.getFileImportanceScore(b.filepath);
return scoreB - scoreA; // Higher score first
});
}
/**
* Calculate importance score for a file path
*/
private getFileImportanceScore(filepath: string): number {
// Source files - highest priority
if (filepath.match(/^(src|lib|app|components|pages|api)\//)) {
return 100;
}
// Test files - high priority
if (filepath.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || filepath.startsWith('test/')) {
return 80;
}
// Configuration files - medium-high priority
if (filepath.match(/\.(json|yaml|yml|toml|config\.(ts|js))$/)) {
return 60;
}
// Documentation - medium priority
if (filepath.match(/\.(md|txt|rst)$/)) {
return 40;
}
// Build artifacts - low priority
if (filepath.match(/^(dist|build|out|\.next|public\/dist)\//)) {
return 10;
}
// Start with default priority
let score = 50;
// Boost interface/type files - they're usually small but critical
if (filepath.includes('interfaces/') || filepath.includes('.types.')) {
score += 20;
}
// Boost entry points
if (filepath.endsWith('index.ts') || filepath.endsWith('mod.ts')) {
score += 15;
}
return score;
}
/**
* Extract head and tail lines from a diff, omitting the middle
*/
private extractDiffSample(fileInfo: IDiffFileInfo, headLines: number, tailLines: number): string {
const lines = fileInfo.diffContent.split('\n');
const totalLines = lines.length;
if (totalLines <= headLines + tailLines) {
// File is small enough to include fully
return fileInfo.diffContent;
}
// Extract file metadata from diff header
const headerLines: string[] = [];
let bodyStartIndex = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('@@')) {
headerLines.push(...lines.slice(0, i + 1));
bodyStartIndex = i + 1;
break;
}
}
const bodyLines = lines.slice(bodyStartIndex);
const head = bodyLines.slice(0, headLines);
const tail = bodyLines.slice(-tailLines);
const omittedLines = bodyLines.length - headLines - tailLines;
const statusEmoji = fileInfo.status === 'added' ? '' :
fileInfo.status === 'deleted' ? '' : '📝';
const parts: string[] = [];
parts.push(`${statusEmoji} FILE: ${fileInfo.filepath}`);
parts.push(`CHANGES: +${fileInfo.linesAdded} lines, -${fileInfo.linesRemoved} lines (${fileInfo.totalLines} total)`);
parts.push('');
parts.push(...headerLines);
parts.push(...head);
parts.push('');
parts.push(`[... ${omittedLines} lines omitted - use Read tool to see full file ...]`);
parts.push('');
parts.push(...tail);
return parts.join('\n');
}
/**
* Get file status prefix with emoji
*/
private getFileStatusPrefix(fileInfo: IDiffFileInfo): string {
const statusEmoji = fileInfo.status === 'added' ? '' :
fileInfo.status === 'deleted' ? '' : '📝';
return `${statusEmoji} `;
}
/**
* Extract filepath from diff content
*/
private extractFilepathFromDiff(diffContent: string): string {
const lines = diffContent.split('\n');
for (const line of lines) {
if (line.startsWith('+++ b/')) {
return line.substring(6);
}
}
return 'unknown';
}
/**
* Format file info as metadata only
*/
private formatMetadataOnly(fileInfo: IDiffFileInfo): string {
const statusEmoji = fileInfo.status === 'added' ? '' :
fileInfo.status === 'deleted' ? '' : '📝';
return `${statusEmoji} ${fileInfo.filepath} (+${fileInfo.linesAdded}, -${fileInfo.linesRemoved})`;
}
/**
* Generate human-readable summary of processed diff
*/
private generateSummary(result: IProcessedDiff): string {
const parts: string[] = [];
parts.push(`Files changed: ${result.totalFiles} total`);
parts.push(`- ${result.fullDiffs.length} included in full`);
parts.push(`- ${result.summarizedDiffs.length} summarized (head/tail shown)`);
parts.push(`- ${result.metadataOnly.length} metadata only`);
parts.push(`Estimated tokens: ~${result.totalTokens.toLocaleString()}`);
if (result.metadataOnly.length > 0) {
parts.push('');
parts.push('NOTE: Some files excluded to stay within token budget.');
parts.push('Use Read tool with specific file paths to see full content.');
}
return parts.join('\n');
}
}

View File

@@ -20,39 +20,39 @@ export class TypeDoc {
public async compile(options?: { publicSubdir?: string }) { public async compile(options?: { publicSubdir?: string }) {
const data = { const data = {
"compilerOptions": { compilerOptions: {
"experimentalDecorators": true, experimentalDecorators: true,
"useDefineForClassFields": false, useDefineForClassFields: false,
"target": "ES2022", target: 'ES2022',
"module": "NodeNext", module: 'NodeNext',
"moduleResolution": "NodeNext", moduleResolution: 'NodeNext',
"esModuleInterop": true, esModuleInterop: true,
"verbatimModuleSyntax": true, verbatimModuleSyntax: true,
"skipLibCheck": true, skipLibCheck: true,
}, },
include: [], include: [],
}; };
let startDirectory = ''; let startDirectory = '';
if (plugins.smartfile.fs.isDirectory(plugins.path.join(paths.cwd, './ts'))) { if (await plugins.fsInstance.directory(plugins.path.join(paths.cwd, './ts')).exists()) {
data.include.push(plugins.path.join(paths.cwd, './ts/**/*')); data.include.push(plugins.path.join(paths.cwd, './ts/**/*'));
startDirectory = 'ts'; startDirectory = 'ts';
} }
if (plugins.smartfile.fs.isDirectory(plugins.path.join(paths.cwd, './ts_web'))) { if (await plugins.fsInstance.directory(plugins.path.join(paths.cwd, './ts_web')).exists()) {
data.include.push(plugins.path.join(paths.cwd, './ts_web/**/*')); data.include.push(plugins.path.join(paths.cwd, './ts_web/**/*'));
if (!startDirectory) { if (!startDirectory) {
startDirectory = 'ts_web'; startDirectory = 'ts_web';
} }
} }
await plugins.smartfile.memory.toFs(JSON.stringify(data), paths.tsconfigFile); await plugins.fsInstance.file(paths.tsconfigFile).encoding('utf8').write(JSON.stringify(data));
let targetDir = paths.publicDir; let targetDir = paths.publicDir;
if (options?.publicSubdir) { if (options?.publicSubdir) {
targetDir = plugins.path.join(targetDir, options.publicSubdir); targetDir = plugins.path.join(targetDir, options.publicSubdir);
} }
await this.smartshellInstance.exec( await this.smartshellInstance.exec(
`typedoc --tsconfig ${paths.tsconfigFile} --out ${targetDir} ${startDirectory}/index.ts` `typedoc --tsconfig ${paths.tsconfigFile} --out ${targetDir} ${startDirectory}/index.ts`,
); );
plugins.smartfile.fs.remove(paths.tsconfigFile); await plugins.fsInstance.file(paths.tsconfigFile).delete();
} }
} }

View File

@@ -28,20 +28,51 @@ export const run = async () => {
}); });
tsdocCli.addCommand('aidoc').subscribe(async (argvArg) => { tsdocCli.addCommand('aidoc').subscribe(async (argvArg) => {
logger.log('info', `Generating new readme...`);
logger.log('info', `This may take some time...`);
const aidocInstance = new AiDoc(); const aidocInstance = new AiDoc();
await aidocInstance.start(); await aidocInstance.start();
aidocInstance.buildReadme(paths.cwd);
logger.log('info', `Generating new readme...`);
logger.log('info', `This may take some time...`);
await aidocInstance.buildReadme(paths.cwd);
logger.log('info', `Generating new keywords...`); logger.log('info', `Generating new keywords...`);
logger.log('info', `This may take some time...`); logger.log('info', `This may take some time...`);
aidocInstance.buildDescription(paths.cwd); await aidocInstance.buildDescription(paths.cwd);
}) });
tsdocCli.addCommand('readme').subscribe(async (argvArg) => {
const aidocInstance = new AiDoc();
await aidocInstance.start();
logger.log('info', `Generating new readme...`);
logger.log('info', `This may take some time...`);
await aidocInstance.buildReadme(paths.cwd);
});
tsdocCli.addCommand('description').subscribe(async (argvArg) => {
const aidocInstance = new AiDoc();
await aidocInstance.start();
logger.log('info', `Generating new description and keywords...`);
logger.log('info', `This may take some time...`);
await aidocInstance.buildDescription(paths.cwd);
});
tsdocCli.addCommand('commit').subscribe(async (argvArg) => {
const aidocInstance = new AiDoc();
await aidocInstance.start();
logger.log('info', `Generating commit message...`);
logger.log('info', `This may take some time...`);
const commitObject = await aidocInstance.buildNextCommitObject(paths.cwd);
logger.log('ok', `Commit message generated:`);
console.log(JSON.stringify(commitObject, null, 2));
});
tsdocCli.addCommand('test').subscribe((argvArg) => { tsdocCli.addCommand('test').subscribe((argvArg) => {
tsdocCli.triggerCommand('typedoc', argvArg); tsdocCli.triggerCommand('typedoc', argvArg);
process.on('exit', async () => { process.on('exit', async () => {
await plugins.smartfile.fs.remove(paths.publicDir); await plugins.fsInstance.directory(paths.publicDir).recursive().delete();
}); });
}); });

View File

@@ -1,15 +1,6 @@
import { commitinfo } from './00_commitinfo_data.js';
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
export const logger = new plugins.smartlog.Smartlog({ export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
logContext: {
company: 'Some Company',
companyunit: 'Some CompanyUnit',
containerName: 'Some Containername',
environment: 'local',
runtime: 'node',
zone: 'gitzone',
},
minimumLogLevel: 'silly',
});
logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal()); logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());

View File

@@ -1,7 +1,10 @@
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
// dirs // dirs
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../'); export const packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../',
);
export const cwd = process.cwd(); export const cwd = process.cwd();
export const binDir = plugins.path.join(packageDir, './node_modules/.bin'); export const binDir = plugins.path.join(packageDir, './node_modules/.bin');
export const assetsDir = plugins.path.join(packageDir, './assets'); export const assetsDir = plugins.path.join(packageDir, './assets');

View File

@@ -6,17 +6,49 @@ export { path };
// pushrocks scope // pushrocks scope
import * as npmextra from '@push.rocks/npmextra'; import * as npmextra from '@push.rocks/npmextra';
import * as qenv from '@push.rocks/qenv'; import * as qenv from '@push.rocks/qenv';
import * as smartagent from '@push.rocks/smartagent';
import * as smartai from '@push.rocks/smartai'; import * as smartai from '@push.rocks/smartai';
import * as smartcli from '@push.rocks/smartcli'; import * as smartcli from '@push.rocks/smartcli';
import * as smartdelay from '@push.rocks/smartdelay'; import * as smartdelay from '@push.rocks/smartdelay';
import * as smartfile from '@push.rocks/smartfile'; import * as smartfile from '@push.rocks/smartfile';
import * as smartfs from '@push.rocks/smartfs';
import * as smartgit from '@push.rocks/smartgit';
import * as smartinteract from '@push.rocks/smartinteract'; import * as smartinteract from '@push.rocks/smartinteract';
import * as smartlog from '@push.rocks/smartlog'; import * as smartlog from '@push.rocks/smartlog';
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local'; import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
import * as smartpath from '@push.rocks/smartpath'; import * as smartpath from '@push.rocks/smartpath';
import * as smartshell from '@push.rocks/smartshell'; import * as smartshell from '@push.rocks/smartshell';
import * as smarttime from '@push.rocks/smarttime';
export { npmextra, qenv, smartai, smartcli, smartdelay, smartfile, smartinteract, smartlog, smartlogDestinationLocal, smartpath, smartshell }; export {
npmextra,
qenv,
smartagent,
smartai,
smartcli,
smartdelay,
smartfile,
smartfs,
smartgit,
smartinteract,
smartlog,
smartlogDestinationLocal,
smartpath,
smartshell,
smarttime,
};
// Create a shared SmartFs instance for filesystem operations
const smartFsNodeProvider = new smartfs.SmartFsProviderNode();
export const fsInstance = new smartfs.SmartFs(smartFsNodeProvider);
// Create a shared SmartFileFactory for in-memory file operations
export const smartfileFactory = smartfile.SmartFileFactory.nodeFs();
// @git.zone scope
import * as tspublish from '@git.zone/tspublish';
export { tspublish };
// third party scope // third party scope
import * as typedoc from 'typedoc'; import * as typedoc from 'typedoc';