notebook-intelligence 4.7.0__tar.gz → 4.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/PKG-INFO +1 -1
  2. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/docs/admin-guide.md +64 -22
  3. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/_version.py +1 -1
  4. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/config.py +72 -5
  5. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/extension.py +99 -0
  6. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/package.json +2 -2
  7. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/schemas/@notebook-intelligence/notebook-intelligence/package.json.orig +1 -1
  8. notebook_intelligence-4.8.0/notebook_intelligence/labextension/static/694.3bcf1107fa397004be17.js +1 -0
  9. notebook_intelligence-4.7.0/notebook_intelligence/labextension/static/728.c8ecc8f6c552988f70cf.js → notebook_intelligence-4.8.0/notebook_intelligence/labextension/static/728.c436df7c2664f4cf8e16.js +1 -1
  10. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/editor.worker.js +2 -2
  11. notebook_intelligence-4.7.0/notebook_intelligence/labextension/static/remoteEntry.556389fd0da7df9338c0.js → notebook_intelligence-4.8.0/notebook_intelligence/labextension/static/remoteEntry.64319537c5af580d82c4.js +1 -1
  12. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/skill_github_import.py +73 -3
  13. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/skill_manifest.py +30 -1
  14. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/package.json +1 -1
  15. notebook_intelligence-4.8.0/tests/test_config_atomic_save.py +104 -0
  16. notebook_intelligence-4.8.0/tests/test_skill_fetch_redirects.py +143 -0
  17. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_skill_github_import.py +6 -6
  18. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_skill_manifest.py +6 -6
  19. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_skills_handlers.py +99 -0
  20. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/notebook-generation.test.ts +1 -1
  21. notebook_intelligence-4.8.0/tests/ts/workspace-skips.test.ts +34 -0
  22. notebook_intelligence-4.7.0/notebook_intelligence/labextension/static/694.cca6e3b729c28358cca4.js +0 -1
  23. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/.copier-answers.yml +0 -0
  24. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/.gitignore +0 -0
  25. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/.prettierignore +0 -0
  26. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/.yarnrc.yml +0 -0
  27. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/CHANGELOG.md +0 -0
  28. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/CONTRIBUTING.md +0 -0
  29. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/LICENSE +0 -0
  30. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/PRIVACY.md +0 -0
  31. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/README.md +0 -0
  32. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/RELEASE.md +0 -0
  33. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/SECURITY.md +0 -0
  34. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/docs/rulesets.md +0 -0
  35. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/docs/skills.md +0 -0
  36. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/docs/troubleshooting.md +0 -0
  37. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/install.json +0 -0
  38. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/jest.config.js +0 -0
  39. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/jupyter-config/server-config/notebook_intelligence.json +0 -0
  40. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/__init__.py +0 -0
  41. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/ai_service_manager.py +0 -0
  42. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/api.py +0 -0
  43. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/base_chat_participant.py +0 -0
  44. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/built_in_toolsets.py +0 -0
  45. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/cell_output.py +0 -0
  46. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/claude.py +0 -0
  47. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/claude_sessions.py +0 -0
  48. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/context_factory.py +0 -0
  49. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/feature_flags.py +0 -0
  50. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/github_copilot.py +0 -0
  51. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/github_copilot_chat_participant.py +0 -0
  52. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/schemas/@notebook-intelligence/notebook-intelligence/plugin.json +0 -0
  53. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/196.db3f5562c0372c6e07f6.js +0 -0
  54. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/1b55f12eca93ba6743e7.ttf +0 -0
  55. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/313.d3668bf2674eafcf4573.js +0 -0
  56. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/327.fe6b84491254fcaf64bf.js +0 -0
  57. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/327.fe6b84491254fcaf64bf.js.LICENSE.txt +0 -0
  58. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/631.a549782dde8c94a5612d.js +0 -0
  59. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/671.1797d409270b24758991.js +0 -0
  60. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/745.54aab870383fe1bf44c8.js +0 -0
  61. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/745.54aab870383fe1bf44c8.js.LICENSE.txt +0 -0
  62. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/830.eecfb0cd62e605cbbd1e.js +0 -0
  63. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/977.7e3d66ffc344a0af7a4e.js +0 -0
  64. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/be02dcbf2787b0b29ec8.module.wasm +0 -0
  65. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/e589c9ba04e3e5a90081.module.wasm +0 -0
  66. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/editor.worker.js.LICENSE.txt +0 -0
  67. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/style.js +0 -0
  68. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/labextension/static/third-party-licenses.json +0 -0
  69. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/llm_providers/github_copilot_llm_provider.py +0 -0
  70. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/llm_providers/litellm_compatible_llm_provider.py +0 -0
  71. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/llm_providers/ollama_llm_provider.py +0 -0
  72. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/llm_providers/openai_compatible_llm_provider.py +0 -0
  73. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/mcp_manager.py +0 -0
  74. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/prompts.py +0 -0
  75. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/rule_injector.py +0 -0
  76. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/rule_manager.py +0 -0
  77. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/ruleset.py +0 -0
  78. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/skill_manager.py +0 -0
  79. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/skill_reconciler.py +0 -0
  80. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/skillset.py +0 -0
  81. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/notebook_intelligence/util.py +0 -0
  82. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/pyproject.toml +0 -0
  83. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/schema/plugin.json +0 -0
  84. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/setup.py +0 -0
  85. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/conftest.py +0 -0
  86. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/01-test-global.md +0 -0
  87. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/02-test-security.md +0 -0
  88. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/03-invalid-rule.md +0 -0
  89. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/modes/agent/01-test-production.md +0 -0
  90. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/modes/ask/01-test-exploration.md +0 -0
  91. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/fixtures/rules/modes/inline-chat/01-test-quick.md +0 -0
  92. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_ai_service_manager_integration.py +0 -0
  93. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_api_hygiene.py +0 -0
  94. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_base_chat_participant_integration.py +0 -0
  95. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_cell_output.py +0 -0
  96. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_cell_output_features_response.py +0 -0
  97. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_claude_client.py +0 -0
  98. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_claude_models.py +0 -0
  99. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_claude_sessions.py +0 -0
  100. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_claude_sessions_handler.py +0 -0
  101. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_config_integration.py +0 -0
  102. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_context_factory.py +0 -0
  103. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_end_to_end_rule_integration.py +0 -0
  104. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_feature_flags.py +0 -0
  105. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_feedback.py +0 -0
  106. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_file_upload.py +0 -0
  107. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_image_context.py +0 -0
  108. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_mcp_manager.py +0 -0
  109. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_models.py +0 -0
  110. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_openai_compatible_llm_provider.py +0 -0
  111. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_rule_auto_reload.py +0 -0
  112. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_rule_injector.py +0 -0
  113. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_rule_manager.py +0 -0
  114. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_skill_manager.py +0 -0
  115. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_skill_reconciler.py +0 -0
  116. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/test_websocket_handler_integration.py +0 -0
  117. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/__mocks__/svg.ts +0 -0
  118. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/__mocks__/tiktoken.ts +0 -0
  119. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/cell-output-bundle.test.ts +0 -0
  120. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/cell-output-features.test.ts +0 -0
  121. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/cell-output-toolbar.test.ts +0 -0
  122. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/mcp-util.test.ts +0 -0
  123. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/setup.ts +0 -0
  124. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/tsconfig.json +0 -0
  125. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tests/ts/utils.test.ts +0 -0
  126. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/tsconfig.json +0 -0
  127. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/webpack.config.js +0 -0
  128. {notebook_intelligence-4.7.0 → notebook_intelligence-4.8.0}/yarn.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notebook_intelligence
3
- Version: 4.7.0
3
+ Version: 4.8.0
4
4
  Summary: AI coding assistant for JupyterLab
5
5
  Project-URL: Homepage, https://github.com/notebook-intelligence/notebook-intelligence
6
6
  Project-URL: Bug Tracker, https://github.com/notebook-intelligence/notebook-intelligence/issues
@@ -75,28 +75,32 @@ If users share a home directory across nodes (NFS-backed shared HPC, classroom l
75
75
 
76
76
  The full surface, in one table.
77
77
 
78
- | Name | Type | Default | Source | Purpose |
79
- | ----------------------------------- | ---- | --------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
80
- | `disabled_providers` | List | `[]` | traitlet on `NotebookIntelligence` | Hide providers from the user dropdown. Values: `github-copilot`, `ollama`, `litellm-compatible`, `openai-compatible`. |
81
- | `allow_enabling_providers_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_PROVIDERS` re-enables hidden providers per pod. |
82
- | `NBI_ENABLED_PROVIDERS` | csv | unset | env | Comma-separated provider IDs to re-enable. Effective only when `allow_enabling_providers_with_env=True`. |
83
- | `disabled_tools` | List | `[]` | traitlet | Hide built-in tools from agent mode. Values listed in [Restricting features](#restricting-features-for-managed-deployments). |
84
- | `allow_enabling_tools_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_BUILTIN_TOOLS` re-enables hidden tools per pod. |
85
- | `NBI_ENABLED_BUILTIN_TOOLS` | csv | unset | env | Comma-separated tool IDs to re-enable. Effective only when `allow_enabling_tools_with_env=True`. |
86
- | `enable_chat_feedback` | Bool | `False` | traitlet | Enables thumbs-up/down UI in chat and emits in-process `telemetry` events. |
87
- | `skills_manifest` | str | `""` | traitlet | URL or filesystem path to a managed-skills manifest. See [`docs/skills.md`](skills.md#managed-skills-via-an-org-manifest). |
88
- | `NBI_SKILLS_MANIFEST` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. |
89
- | `skills_manifest_interval` | int | `86400` | traitlet | Seconds between reconciles. |
90
- | `NBI_SKILLS_MANIFEST_INTERVAL` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. |
91
- | `managed_skills_token` | str | `""` | traitlet | Bearer token for managed-skills GitHub fetches. |
92
- | `NBI_MANAGED_SKILLS_TOKEN` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. |
93
- | `NBI_GH_ACCESS_TOKEN_PASSWORD` | str | `nbi-access-token-password` | env | Password used to encrypt the stored Copilot token in `user-data.json`. **Change in multi-tenant deployments.** |
94
- | `NBI_RULES_AUTO_RELOAD` | bool | `true` | env | When `false`, ruleset edits require a JupyterLab restart to take effect. |
95
- | `NBI_CLAUDE_CLI_PATH` | str | unset | env | Absolute path to the Claude Code CLI binary. When unset, NBI looks up `claude` on `PATH`. |
96
- | `NBI_GHE_SUBDOMAIN` | str | `""` | env | GitHub Enterprise subdomain for GitHub Copilot users on a GHE tenant. Empty selects github.com. |
97
- | `NBI_LOG_LEVEL` | str | `INFO` | env | Python logging level for the `notebook_intelligence` logger. |
98
- | `GITHUB_TOKEN`, `GH_TOKEN` | str | unset | env | Used (in that order) by user-initiated skill imports for GitHub auth. Falls back to `gh` CLI auth. |
99
- | `NBI_*_POLICY` | str | `user-choice` | env | Lock individual Settings panel toggles. See [README → Admin policies](../README.md#admin-policies) for the full list of `*_POLICY` env vars and matching traitlets. |
78
+ | Name | Type | Default | Source | Purpose |
79
+ | ---------------------------------------------- | ---- | --------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
80
+ | `disabled_providers` | List | `[]` | traitlet on `NotebookIntelligence` | Hide providers from the user dropdown. Values: `github-copilot`, `ollama`, `litellm-compatible`, `openai-compatible`. |
81
+ | `allow_enabling_providers_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_PROVIDERS` re-enables hidden providers per pod. |
82
+ | `NBI_ENABLED_PROVIDERS` | csv | unset | env | Comma-separated provider IDs to re-enable. Effective only when `allow_enabling_providers_with_env=True`. |
83
+ | `disabled_tools` | List | `[]` | traitlet | Hide built-in tools from agent mode. Values listed in [Restricting features](#restricting-features-for-managed-deployments). |
84
+ | `allow_enabling_tools_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_BUILTIN_TOOLS` re-enables hidden tools per pod. |
85
+ | `NBI_ENABLED_BUILTIN_TOOLS` | csv | unset | env | Comma-separated tool IDs to re-enable. Effective only when `allow_enabling_tools_with_env=True`. |
86
+ | `enable_chat_feedback` | Bool | `False` | traitlet | Enables thumbs-up/down UI in chat and emits in-process `telemetry` events. |
87
+ | `additional_skipped_workspace_directories` | List | `[]` | traitlet | Extra directory names to skip in the chat-sidebar @-mention workspace file picker. Merged with the built-in skips (`__pycache__`, `node_modules`). Match is by directory name only, case-sensitive. |
88
+ | `NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES` | csv | unset | env (appends to traitlet) | Comma-separated extra directory names. Resolved at server startup and concatenated with the traitlet value, so a spawn profile can add to (rather than replace) the org-wide list. |
89
+ | `allow_github_skill_import` | Bool | `True` | traitlet | When `False`, hides the **Import from GitHub** button in the Skills panel and rejects `/skills/import` POSTs with 403. Does not affect the managed-skills reconciler. |
90
+ | `NBI_ALLOW_GITHUB_SKILL_IMPORT` | bool | unset | env (overrides traitlet) | Per-pod override for `allow_github_skill_import`. Accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off` (case-insensitive). Useful for varying the policy across spawn profiles. |
91
+ | `skills_manifest` | str | `""` | traitlet | URL or filesystem path to a managed-skills manifest. See [`docs/skills.md`](skills.md#managed-skills-via-an-org-manifest). |
92
+ | `NBI_SKILLS_MANIFEST` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. |
93
+ | `skills_manifest_interval` | int | `86400` | traitlet | Seconds between reconciles. |
94
+ | `NBI_SKILLS_MANIFEST_INTERVAL` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. |
95
+ | `managed_skills_token` | str | `""` | traitlet | Bearer token for managed-skills GitHub fetches. |
96
+ | `NBI_MANAGED_SKILLS_TOKEN` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. |
97
+ | `NBI_GH_ACCESS_TOKEN_PASSWORD` | str | `nbi-access-token-password` | env | Password used to encrypt the stored Copilot token in `user-data.json`. **Change in multi-tenant deployments.** |
98
+ | `NBI_RULES_AUTO_RELOAD` | bool | `true` | env | When `false`, ruleset edits require a JupyterLab restart to take effect. |
99
+ | `NBI_CLAUDE_CLI_PATH` | str | unset | env | Absolute path to the Claude Code CLI binary. When unset, NBI looks up `claude` on `PATH`. |
100
+ | `NBI_GHE_SUBDOMAIN` | str | `""` | env | GitHub Enterprise subdomain for GitHub Copilot users on a GHE tenant. Empty selects github.com. |
101
+ | `NBI_LOG_LEVEL` | str | `INFO` | env | Python logging level for the `notebook_intelligence` logger. |
102
+ | `GITHUB_TOKEN`, `GH_TOKEN` | str | unset | env | Used (in that order) by user-initiated skill imports for GitHub auth. Falls back to `gh` CLI auth. |
103
+ | `NBI_*_POLICY` | str | `user-choice` | env | Lock individual Settings panel toggles. See [README → Admin policies](../README.md#admin-policies) for the full list of `*_POLICY` env vars and matching traitlets. |
100
104
 
101
105
  Configure traitlets in `jupyter_server_config.py`:
102
106
 
@@ -315,6 +319,44 @@ NBI_ENABLED_BUILTIN_TOOLS=nbi-notebook-execute,nbi-python-file-edit
315
319
 
316
320
  NBI does not currently support an explicit allowlist mode (`allowed_providers`, `allowed_tools`). A new built-in provider added in a minor release would auto-enable for users with `disabled_providers=[]`. If this matters for your compliance posture, pin to specific NBI versions and review changelog entries before upgrading. Tracked as a feature request.
317
321
 
322
+ ### Disabling user-initiated GitHub Skill imports
323
+
324
+ ```python
325
+ c.NotebookIntelligence.allow_github_skill_import = False
326
+ ```
327
+
328
+ Hides the **Import from GitHub** button in the Skills panel and rejects POSTs to `/notebook-intelligence/skills/import` and `/notebook-intelligence/skills/import/preview` with HTTP 403. This does **not** disable the [managed-skills reconciler](#managed-claude-skills-token); admin-curated skills delivered via `NBI_SKILLS_MANIFEST` continue to install. Use this when you want to allow only org-vetted skills.
329
+
330
+ To vary the policy per spawn profile, override at pod startup:
331
+
332
+ ```bash
333
+ NBI_ALLOW_GITHUB_SKILL_IMPORT=false
334
+ ```
335
+
336
+ The env var wins over the traitlet and is resolved at server startup. Recognized values: `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off`, case-insensitive. Unrecognized values raise at startup so a typo can't silently flip the policy.
337
+
338
+ ### Tuning the chat-sidebar workspace file picker
339
+
340
+ **Audience:** server admins. End users can't override this — ask your admin to add or remove entries.
341
+
342
+ The @-mention picker in the chat sidebar enumerates files from the JupyterLab working directory and skips a built-in set of directories (`__pycache__`, `node_modules`) plus any dotfiles/dot-directories. Because dot-prefixed names are filtered separately, entries starting with `.` in the list below are no-ops; list non-dot names only.
343
+
344
+ To extend the directory skip set without editing the codebase:
345
+
346
+ ```python
347
+ c.NotebookIntelligence.additional_skipped_workspace_directories = ["build", "dist", "target"]
348
+ ```
349
+
350
+ Match is by directory name only (not path), case-sensitive. Use this when a project has standard build outputs the picker shouldn't surface.
351
+
352
+ To vary the list per spawn profile, append entries via env at pod startup:
353
+
354
+ ```bash
355
+ NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES=tmp,artifacts
356
+ ```
357
+
358
+ The env var **appends** to the traitlet value at server startup (in contrast to `NBI_ALLOW_GITHUB_SKILL_IMPORT` and the `NBI_*_POLICY` env vars, which override). Duplicates are collapsed; both lists are then merged with the built-in skip set on the frontend.
359
+
318
360
  ---
319
361
 
320
362
  ## Multi-tenancy and per-team scoping
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '4.7.0'
4
+ __version__ = VERSION = '4.8.0'
@@ -3,7 +3,9 @@
3
3
  import json
4
4
  import logging
5
5
  import os
6
+ import stat
6
7
  import sys
8
+ import tempfile
7
9
  from typing import Optional
8
10
 
9
11
  from notebook_intelligence.feature_flags import (
@@ -94,11 +96,8 @@ class NBIConfig:
94
96
  # TODO: save only diff
95
97
  os.makedirs(self.nbi_user_dir, exist_ok=True)
96
98
 
97
- with open(self.user_config_file, 'w') as file:
98
- json.dump(self.user_config, file, indent=2)
99
-
100
- with open(self.user_mcp_file, 'w') as file:
101
- json.dump(self.user_mcp, file, indent=2)
99
+ _atomic_write_json(self.user_config_file, self.user_config)
100
+ _atomic_write_json(self.user_mcp_file, self.user_mcp)
102
101
 
103
102
  def get(self, key, default=None):
104
103
  return self.user_config.get(key, self.env_config.get(key, default))
@@ -211,3 +210,71 @@ class NBIConfig:
211
210
  active_rules = self.active_rules.copy()
212
211
  active_rules[filename] = active
213
212
  self.set('active_rules', active_rules)
213
+
214
+
215
+ def _atomic_write_json(target: str, payload: dict) -> None:
216
+ """Crash-safe replacement for ``open(target, 'w') + json.dump``.
217
+
218
+ Plain truncating writes leave the destination empty (or partial)
219
+ if the process is killed mid-write — the next launch then chokes on
220
+ a corrupt config. Write to a sibling tempfile, ``fsync``, and
221
+ ``os.replace`` so the swap is atomic on POSIX and atomic-ish on
222
+ Windows.
223
+
224
+ Symlink-preserving: a user who symlinks ``~/.jupyter/nbi/config.json``
225
+ to a shared config file expects ``save()`` to update the link's
226
+ target, not replace the link itself. Resolve via ``realpath`` first.
227
+
228
+ Mode-preserving: ``mkstemp`` returns a 0o600 file; if the existing
229
+ config has different permissions (e.g. 0o644 for a shared install),
230
+ re-apply them after the swap so the user's chmod isn't silently
231
+ undone.
232
+
233
+ Durability: ``fsync`` the tempfile, then ``fsync`` the target's
234
+ parent directory on POSIX so the rename itself survives a crash.
235
+ """
236
+ real_target = os.path.realpath(target)
237
+ target_dir = os.path.dirname(real_target) or '.'
238
+ existing_mode = None
239
+ try:
240
+ existing_mode = stat.S_IMODE(os.stat(real_target).st_mode)
241
+ except FileNotFoundError:
242
+ pass
243
+ fd, tmp_path = tempfile.mkstemp(
244
+ prefix='.' + os.path.basename(real_target) + '.',
245
+ suffix='.tmp',
246
+ dir=target_dir,
247
+ )
248
+ try:
249
+ with os.fdopen(fd, 'w', encoding='utf-8') as fh:
250
+ json.dump(payload, fh, indent=2)
251
+ fh.flush()
252
+ os.fsync(fh.fileno())
253
+ if existing_mode is not None:
254
+ try:
255
+ os.chmod(tmp_path, existing_mode)
256
+ except OSError:
257
+ # Some filesystems (FAT, certain network mounts) reject chmod;
258
+ # the swap itself is still safe, just lose the mode bits.
259
+ pass
260
+ os.replace(tmp_path, real_target)
261
+ except Exception:
262
+ # Best-effort cleanup — if replace failed the tempfile is dangling.
263
+ try:
264
+ os.unlink(tmp_path)
265
+ except OSError:
266
+ pass
267
+ raise
268
+ # ``os.replace`` makes the new inode visible, but until the directory
269
+ # entry is fsynced a power-loss can still leave the rename unrecorded
270
+ # on POSIX. Windows has no ``O_RDONLY`` directory descriptor, so skip
271
+ # there and rely on its rename semantics.
272
+ if hasattr(os, 'O_DIRECTORY'):
273
+ try:
274
+ dir_fd = os.open(target_dir, os.O_RDONLY)
275
+ try:
276
+ os.fsync(dir_fd)
277
+ finally:
278
+ os.close(dir_fd)
279
+ except OSError:
280
+ pass
@@ -155,6 +155,45 @@ def _resolve_policy_with_env(env_var_name: str, traitlet_value: str) -> str:
155
155
  return traitlet_value
156
156
 
157
157
 
158
+ _TRUE_VALUES = frozenset({"true", "1", "yes", "on"})
159
+ _FALSE_VALUES = frozenset({"false", "0", "no", "off"})
160
+ _BOOL_ENV_VOCAB = "true, false, 1, 0, yes, no, on, off"
161
+
162
+
163
+ def _resolve_csv_appended(env_var_name: str, traitlet_value):
164
+ """Merge a traitlet List with the comma-separated env-var value (append).
165
+
166
+ Env *adds to* the traitlet list rather than replacing it — the use case
167
+ is per-pod profiles layering on an org-wide baseline. Whitespace around
168
+ each token is stripped, empty segments are dropped, and exact duplicates
169
+ are collapsed while preserving first-seen order.
170
+ """
171
+ base = list(traitlet_value or [])
172
+ raw = os.environ.get(env_var_name, "")
173
+ extras = [name.strip() for name in raw.split(",") if name.strip()]
174
+ return list(dict.fromkeys(base + extras))
175
+
176
+
177
+ def _resolve_bool_with_env(env_var_name: str, fallback: bool | None) -> bool:
178
+ """Resolve a boolean admin gate: env var wins if recognized, else fallback.
179
+
180
+ Raises ValueError when the env var is set but unrecognized — silent
181
+ fall-back can flip a security gate either direction depending on the
182
+ fallback polarity, so a typo must surface at startup. ``None`` fallback
183
+ is coerced to ``False``.
184
+ """
185
+ env_value = os.environ.get(env_var_name, "").strip().lower()
186
+ if not env_value:
187
+ return bool(fallback)
188
+ if env_value in _TRUE_VALUES:
189
+ return True
190
+ if env_value in _FALSE_VALUES:
191
+ return False
192
+ raise ValueError(
193
+ f"Invalid {env_var_name}={env_value!r}: must be one of {_BOOL_ENV_VOCAB}"
194
+ )
195
+
196
+
158
197
  # Single source of truth for the boolean policies. Each entry is
159
198
  # ``(policy_name, env_var, traitlet_attr)``. Drives env-var resolution, the
160
199
  # capabilities response, and the lock-rejection set in ConfigHandler.
@@ -279,6 +318,8 @@ class GetCapabilitiesHandler(APIHandler):
279
318
  disabled_providers = []
280
319
  allow_enabling_providers_with_env = False
281
320
  enable_chat_feedback = False
321
+ allow_github_skill_import = True
322
+ additional_skipped_workspace_directories = []
282
323
  feature_policies = {}
283
324
  string_overrides = {}
284
325
 
@@ -365,6 +406,8 @@ class GetCapabilitiesHandler(APIHandler):
365
406
  "claude_cli_available": shutil.which("claude") is not None,
366
407
  "default_chat_mode": nbi_config.default_chat_mode,
367
408
  "chat_feedback_enabled": self.enable_chat_feedback,
409
+ "allow_github_skill_import": self.allow_github_skill_import,
410
+ "additional_skipped_workspace_directories": self.additional_skipped_workspace_directories,
368
411
  "cell_output_features": _build_cell_output_features_response(
369
412
  self.feature_policies.get("explain_error", POLICY_USER_CHOICE),
370
413
  self.feature_policies.get("output_followup", POLICY_USER_CHOICE),
@@ -654,10 +697,19 @@ class RulesReloadHandler(APIHandler):
654
697
  class SkillsBaseHandler(APIHandler):
655
698
  """Shared helpers for skills endpoints."""
656
699
 
700
+ allow_github_skill_import = True
701
+
657
702
  @property
658
703
  def skill_manager(self):
659
704
  return ai_service_manager.get_skill_manager()
660
705
 
706
+ def _reject_if_github_import_disabled(self) -> bool:
707
+ if self.allow_github_skill_import:
708
+ return False
709
+ self.set_status(403)
710
+ self.finish(json.dumps({"error": "GitHub Skill import is disabled by configuration"}))
711
+ return True
712
+
661
713
  def _error(self, exc):
662
714
  if isinstance(exc, FileExistsError):
663
715
  self.set_status(409)
@@ -765,6 +817,8 @@ class SkillDetailHandler(SkillsBaseHandler):
765
817
  class SkillsImportPreviewHandler(SkillsBaseHandler):
766
818
  @tornado.web.authenticated
767
819
  def post(self):
820
+ if self._reject_if_github_import_disabled():
821
+ return
768
822
  data = self._parse_json_body()
769
823
  if data is None:
770
824
  return
@@ -783,6 +837,8 @@ class SkillsImportPreviewHandler(SkillsBaseHandler):
783
837
  class SkillsImportHandler(SkillsBaseHandler):
784
838
  @tornado.web.authenticated
785
839
  def post(self):
840
+ if self._reject_if_github_import_disabled():
841
+ return
786
842
  data = self._parse_json_body()
787
843
  if data is None:
788
844
  return
@@ -1653,6 +1709,38 @@ class NotebookIntelligence(ExtensionApp):
1653
1709
  config=True,
1654
1710
  )
1655
1711
 
1712
+ allow_github_skill_import = Bool(
1713
+ default_value=True,
1714
+ help="""
1715
+ Allow importing Skills from GitHub via the Skills panel. Set to False
1716
+ to hide the "Import from GitHub" affordance and reject backend imports.
1717
+ Overridden by the NBI_ALLOW_GITHUB_SKILL_IMPORT env var.
1718
+ """,
1719
+ allow_none=True,
1720
+ config=True,
1721
+ )
1722
+
1723
+ additional_skipped_workspace_directories = List(
1724
+ trait=Unicode(),
1725
+ default_value=None,
1726
+ help="""
1727
+ Extra directory names to skip when enumerating workspace files for
1728
+ the chat-sidebar @-mention picker. Merged with the built-in skip set
1729
+ (`__pycache__`, `node_modules`); dotfiles and dot-directories are
1730
+ already filtered separately, so entries starting with `.` are no-ops.
1731
+
1732
+ The NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES env var (csv)
1733
+ appends to this list at server startup so spawn profiles can vary
1734
+ the policy without forking config.
1735
+
1736
+ Match is by directory name only (not path), case-sensitive.
1737
+
1738
+ Example: ['build', 'dist', 'target']
1739
+ """,
1740
+ allow_none=True,
1741
+ config=True,
1742
+ )
1743
+
1656
1744
  explain_error_policy = TraitletEnum(
1657
1745
  list(VALID_POLICIES),
1658
1746
  default_value=POLICY_USER_CHOICE,
@@ -1903,6 +1991,17 @@ class NotebookIntelligence(ExtensionApp):
1903
1991
  GetCapabilitiesHandler.disabled_providers = self.disabled_providers
1904
1992
  GetCapabilitiesHandler.allow_enabling_providers_with_env = self.allow_enabling_providers_with_env
1905
1993
  GetCapabilitiesHandler.enable_chat_feedback = self.enable_chat_feedback
1994
+ allow_github_skill_import = _resolve_bool_with_env(
1995
+ "NBI_ALLOW_GITHUB_SKILL_IMPORT", self.allow_github_skill_import
1996
+ )
1997
+ GetCapabilitiesHandler.allow_github_skill_import = allow_github_skill_import
1998
+ SkillsBaseHandler.allow_github_skill_import = allow_github_skill_import
1999
+ GetCapabilitiesHandler.additional_skipped_workspace_directories = (
2000
+ _resolve_csv_appended(
2001
+ "NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES",
2002
+ self.additional_skipped_workspace_directories,
2003
+ )
2004
+ )
1906
2005
  self._publish_policies(feature_policies, string_overrides)
1907
2006
  NotebookIntelligence.handlers = [
1908
2007
  (route_pattern_capabilities, GetCapabilitiesHandler),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notebook-intelligence/notebook-intelligence",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
4
  "description": "AI coding assistant for JupyterLab",
5
5
  "keywords": [
6
6
  "AI",
@@ -142,7 +142,7 @@
142
142
  }
143
143
  },
144
144
  "_build": {
145
- "load": "static/remoteEntry.556389fd0da7df9338c0.js",
145
+ "load": "static/remoteEntry.64319537c5af580d82c4.js",
146
146
  "extension": "./extension",
147
147
  "style": "./style"
148
148
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notebook-intelligence/notebook-intelligence",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
4
  "description": "AI coding assistant for JupyterLab",
5
5
  "keywords": [
6
6
  "AI",